Scala руководство пользователя

Scala – строгий статически типизированный JVM-based язык, успешно совмещающий парадигмы объектно-ориентированного и функционального программирования. В языке есть классы, функции высшего порядка, анонимные функции, обобщенное программирование. Использование Java-кода из Scala не вызывает трудностей, синтаксически языки очень близки. В этой статье мы разберем основные элементы языка, достаточные для того, чтобы начать на нем писать.

Настройка окружения

Scala — язык, работающий на JVM, поэтому для работы требует установленную JDK (минимальная версия 1.6). Ее можно взять отсюда. После установки JDK можно приступить к установке самой Scala. Скачать свежую версию можно на официальном сайте. Последняя версия на момент написания статьи — 2.11.6.

Для того, чтобы все корректно работало из командной строки, рекомендуется прописать переменные среды JAVA_HOME и SCALA_HOME, а также дополнить переменную PATH путями к выполняемым файлам. На Linux и MacOS это делается так:

export JAVA_HOME=<путь к каталогу c установленной Java, в котором есть папка 'bin'>
export SCALA_HOME=<путь к каталогу c установленной Scala, в котором есть папка 'bin'>
export PATH=$PATH:$JAVA_HOME/bin:$SCALA_HOME/bin

Для того, чтобы сохранить эти настройки, их надо прописать в ~/.bashrc или ~/.bash_profile.

На Windows команда немного другая:

set JAVA_HOME=<путь к каталогу c установленной Java, в котором есть папка 'bin'>
set SCALA_HOME=<путь к каталогу c установленной Scala, в котором есть папка 'bin'>
set PATH=%PATH%;%JAVA_HOME%bin;%SCALA_HOME%bin

Прописать эти опции постоянно можно в настройках системы: Control Panel → Advanced System Settings → Environmental Variables.

После выполнения всех манипуляций можно проверить результат, запустив:

> java -version
java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)

> scala -version
Scala code runner version 2.11.6 -- Copyright 2002-2013, LAMP/EPFL

SBT

Простые скрипты и маленькие программы можно, конечно, компилировать и запускать вручную с помощью команд scalac и scala. Однако, по мере того, как количество файлов будет расти, ручная компиляция будет становиться все более нудной. Вместо этого используют системы сборки. Для сборки кода на Scala можно использовать стандартные для Java (неофициально) maven, gradle или ant, но сообщество и сами разработчики рекомендуют sbt (simple build tool).

Примечание: если вы устанавливаете sbt, то можете пропустить отдельную установку scala, так как система сборки скачает ее автоматически

Описание процесса сборки находится либо в файле build.sbt в корне проекта, либо в файлах .scala в папке project там же. Само описание – это программа на Scala (которая, в свою очередь, может собираться с помощью sbt как отдельный проект, который… ну, вы поняли).

Синтаксис .sbt-файла напоминает синтаксис Scala с некоторыми дополнениями и ограничениями. Минимальный build.sbt выглядит примерно так (пустые строки обязательны):

name := "My Project"

version := "0.1.0"

scalaVersion := "2.11.6"

libraryDependencies ++= Seq(
  "org.scalatest" %% "scalatest" % "1.6.1" % "test"
)

Исходники помещаются в папку src/main/scala и src/test/scala по пути, соответствующем иерархии пакетов (как в Java). Чтобы собрать, протестировать и запустить проект, необходимо в любой поддиректории проекта выполнить следующие команды:

> sbt compile
> sbt test
> sbt run

или через интерактивную консоль:

> sbt
sbt> compile
sbt> test
sbt> run

Последовательное выполнение команд выглядит немного необычно (обратите внимание на точку с запятой в начале — это особенность синтаксиса):

> sbt
sbt> ; compile; test; run

REPL

Отличным помощником в разработке будет REPL (Read-Eval-Print-Loop), или по-другому, интерактивная консоль. Очень удобно проверять в ней небольшие функции, отлаживать код или просто посмотреть возможности языка. Для запуска REPL наберите sbt console в командной строке. Вы увидите примерно следующее:

> sbt console
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
[info] Set current project to test (in build file:/C:/Users/foxmk/Desktop/test)
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_25).
Type in expressions to have them evaluated.
Type :help for more information.

scala>

Все! Можно писать команды на Scala и сразу же их выполнять:

scala> 1 + 1
res1: Int = 2

scala> def f(x: Int) = x * 2
f: (x: Int)Int

scala> f(4)
res2: Int = 8

scala> println("Hello!")
Hello!

scala>

Для выхода из REPL можно нажать Ctrl+D. Все примеры на Scala далее можно протестировать в REPL, для вставки больших кусков кода можно воспользоваться командой :paste.

IDE

Использование IDE для разработки на Scala не обязательно, однако сильно упрощает процесс. Скала — язык со сложной семантикой, поэтому возможности IDE более ограничены, чем, скажем, при разработке на Java. Тем не менее даже простая подсветка несуществующих методов и автодополнение существующих может сильно облегчить жизнь. Самые популярные IDE для Scala — это IntelliJ IDEA и Eclipse. Для IDEA есть плагин от JetBrains, в случае с Eclipse есть ее вариант Scala IDE.

Переменные, значения и типы.

В Scala переменные и значения объявляются ключевым словом val или var. val — это неизменяемая переменная (значение), аналог final в Java. var — обычная переменная. Например:

> val x = "Some immutable string"
> var y = "I CAN CHANGE THIS!"

> y = "WHOA! I HAVE CHANGED THIS!"
> x = "OOPS!"
Error:(3, 4) reassignment to val
x = "OOPS!"};}
^

Аналогичный код на Java будет выглядеть так:

public final String x = "Some immutable string";
public String y = "I CAN CHANGE THIS!";

y = "WHOA! I HAVE CHANGED THIS!";
x = "OOPS!"; // Ошибка компиляции

Здесь мы видим сразу несколько приятных особенностей Scala:

  1. точка с запятой не обязательна (работает автоматический вывод);
  2. указания типа переменной необязательно (также работает автоматический вывод типов);
  3. ключевое слово public подразумевается по умолчанию.

Типы переменных указываются после имени, через двоеточие. Также в Scala нет, как таковых, примитивных типов (int, float, boolean и т.д.). Их заменяют соответствующие классы Int, Float, Boolean и т.д. Любая переменная — экземпляр какого-либо класса. Иерархия классов начинается с Any, все классы наследуются от него (аналог Object в Java).

val i: Int = 0 // Scala
public int i = 0; // Java

val flag: Boolean = true // Scala
public boolean flag = true; // Java

Применение привычных операторов, при этом, на самом деле — вызов метода: a + b тождественно a.+(b). Вариант записи без точки применим к любым методам (с некоторыми ограничениями).

Функции, анонимные функции, методы

Функция в Scala объявляется с помощью ключевого слова def. Пример объявления и применения функции:

def addOne(a: Int) = a + 1

addOne(1)
// res0: Int = 2

Аналогичный код на Java:

public int addOne(int a) {
  return a + 1;
}

Как видно на примере, необязательны не только точка с запятой и указание типа, но и фигурные скобки вокруг единственного выражения и слово return. Более того, его использование считается плохой практикой. Из функции возвращается значение последней выполненной команды.

На самом деле, функция — это тоже объект. Каждая функция в Scala — это экземпляр класса Function, у которого есть метод apply. Поэтому мы вполне можем записать так (знак подчеркивания ставится на место аргумента функции):

val functionValue = addOne _

functionValue.apply(1)
// res1: Int = 2

Вызов метода apply подразумевается по умолчанию, поэтому использование функций внешне выглядит как в Java:

functionValue(1)
// res2: Int = 2

Все четыре вызова функции идентичны. Представление функций в виде объектов позволяет оперировать с ними, как с остальными объектами: передавать в качестве аргументов, возвращать из других функций, расширять дополнительными методами и т.д., что позволяет Scala полноценно поддерживать парадигму функционального программирования.

Конечно, присутствуют анонимные функции (лямбда-функции). Они объявляются так:

val f = (x: Int) => x + 1

f(1)
// res3: Int = 2

Здесь мы объявляем анонимную функцию, которая принимает один целочисленный аргумент и присвоил ее переменной f, после чего применяем f как обычную функцию.

Классы и объекты

Если вы программировали на Java, многие вещи, касающиеся объектно-ориентированного программирования будут вам знакомы. Класс объявляется ключевым словом class, новый экземпляр — через new. Методы класса — это функции, объявленные в его теле. Поля класса указываются сразу после имени, как список аргументов. При этом, по умолчанию они объявляются как private val. То есть, если мы не укажем никаких модификаторов, указанное поле будет доступно только внутри класса и будет неизменяемым. Класс можно сделать абстрактным, добавив abstract перед объявлением. Основное отличие от Java здесь заключается в отсутствии конструктора. Код, который должен выполняться при создании объекта, пишется прямо в теле класса. Как при этом реализовать несколько конструкторов с различным числом аргументов, мы рассмотрим позже. Пример использования класса:

class Foo(x: Int) {
  def bar(y: Int) = x + y
}

val foo = new Foo(1)
// foo: Foo = Foo@69007238

foo.bar(1)
// res4: Int = 2

И аналогичный код на Java:

class Foo {
  private int x_;

  Foo(int x) {
    _x = x;
  }

  public int bar(int y) {
    return x + y;
  }
}

Foo foo = new Foo(1)

int result = foo.bar(1)
// result теперь равен 2

Как видим, public указывать не обязательно, аргументы конструктора доступны во всем классе, локальное приватное поле создавать также не обязательно.

Кроме того, в Scala мы можем объявить сразу объект, без создания класса, с помощью ключевого слова object. Таким образом реализуется паттерн «Одиночка» (Singleton).

object Singleton(field: String) {
  def squareMe(x: Int) = x*x
}

Singleton.squareMe(2)
// res5: Int = 4

Аналог на Java будет куда более многословен.

public class Singleton {
  private static Singleton instance = null;

  protected Singleton() {
    // Exists only to defeat instantiation.
  }

  public static Singleton getInstance() {
    if(instance == null) {
      instance = new Singleton();
    }
    return instance;
  }

  public int squareMe(int x) {
    return x*x;
  }
}

int a = Singleton.getInstance().squareMe(2)
// a теперь равен 4

В этом примере мы пометили конструктор как protected, чтобы исключить возможность его вызова извне, обращение к объекту будет осуществляться через метод getInstance(), который при первом своем вызове инициализирует экземпляр класс, а при последующих возвращает уже созданный экземпляр. Кроме того, вполне допустимо существование объекта и класса с одним и тем же именем, при этом они делят область видимости. Поэтому необходимость в директиве static отпадает — методы, объявленные не в классе, а в объекте ведут себя как статические. Такой объект называется в терминологии Scala «companion object» («объект-компаньон»).

Вернемся к конструкторам. Вспомним, что при применении любого объекта к некоторым аргументам по умолчанию вызывается метод apply. Этим мы и воспользуемся и напишем класс с несколькими конструкторами, статическими методами, изменяемыми и неизменяемыми полями в идиоматичном для Scala стиле и продублируем этот же код на Java.

Вариант Scala:

object MyUselessClass {
  def staticMethod(x: Int) = x + 5

  def apply(immutableField: Int): MyUselessClass = new MyUselessClass(immutableField, 2)

  def apply(immutableField: Int, mutableField: Int): MyUselessClass = new MyUselessClass(immutableField, mutableField)

  def apply(immutableField: Int, mutableField: Int, privateField: Int): MyUselessClass = new MyUselessClass(immutableField, mutableField, privateField)
}

class MyUselessClass(val immutableField: Int, var mutableField: Int, privateField: Int = 8 /*значние по умолчанию*/) {
  def instanceMethod() = {
    val sumOfFields = immutableField + mutableField + privateField
    MyUselessClass.staticMethod(sumOfFields)
  }
}

// Первый конструктор, обратите внимание на отсутствие 'new',
// так как это на самом деле вызов метода 'apply'
val myUselessObject = MyUselessClass(1)

// аналогично предыдущему варианту
val myAnotherUselessObject = MyUselessClass.apply(1)

// Третий конструктор
val myThirdUselessObject = MyUselessClass(1, 2, 3)

// Вызов метода
myUselessObject.instanceMethod()
// res6: Int = 16

// Поля доступны также, как методы
myUselessObject.mutableField
// res7: Int = 2
myUselessObject.immutableField
// res8: Int = 1
myUselessObject.mutableField = 9
myUselessObject.mutableField
// res9: Int = 9

// Вызов статического метода
MyUselessClass.staticMethod(3)
// res10: Int = 8

Вариант Java:

public class MyUselessClass {
  private int immutableField_;
  private int mutableField_;
  private int privateField_ = 8;

  MyUselessClass(int immutableField) {
    immutableField_ = immutableField;
    mutableField_ = 2;
  }

  MyUselessClass(int immutableField, int mutableField) {
    immutableField_ = immutableField;
    mutableField_ = mutableField;
  }

  MyUselessClass(int immutableField, int immutableField, int privateField) {
    immutableField_ = immutableField;
    mutableField_ = mutableField;
    privateField_ = privateField;
  }

  int getImmutableField() {
    return immutableField;
  }

  int getMutableField() {
    return mutableField;
  }

  void setMutableField(int newValue) {
    mutableField = newValue;
  }

  public static int staticMethod(int x) {
    return x + 5;
  }

  public int instanceMethod() {
    int sumOfFields = immutableField + mutableField + privateField;
    return staticMethod(sumOfFields);
  }
}

// Первый конструктор
MyUselessClass myUselessObject = new MyUselessClass(1)

// Третий конструктор
MyUselessClass myAnotherUselessObject = new MyUselessClass(1, 2, 3)

// Вызов метода
myUselessObject.instanceMethod()
// вернет 16

// Поля доступны также, как методы
myUselessObject.getMutableField
// вернет 2

myUselessObject.getImmutableField
// вернет 1

myUselessObject.setMutableField(9)
myUselessObject.getMmutableField
// вернет 9

// Вызов статического метода
MyUselessClass.staticMethod(3)
// вернет 8

Интерфейсы и трейты

Аналогом Java-интерфейса в Scala является трейт (trait). Как ни удивительно, объявляется он с помощью ключевого слова trait. Как и интерфейсы Java, трейты содержат только объявления методов и допускают множественное наследование. В отличие от интерфейса, в трейте можно описывать поля класса и частично реализовывать методы. Наследование как трейтов, так и абстрактных классов осуществляется с помощью extend (первый родитель) и with (последующие родители). Пример использования:

trait FirstTrait {
  def foo(x: Int)
}

trait SecondTrait {
  def bar(y: Int) = y + 5
}

class ComplexClass extends FirstTrait with SecondTrait {
  override def foo(x: Int) = x * 2
}

Ключевое слово override необязательно, но его использование является хорошей практикой.

Другие особенности и отличия от Java

Как и в Java, в Scala классы, трейты и функции можно параметризовать. Параметры типов пишутся в квадратных скобках после имени класса или функции. Так определяется интерфейс Foo, который принимает некоторый тип A и содержит метод bar? который принимает значение типа A и некоторого типа B и возвращает объект типа C. Конкретные типы A, B и C будут определены в реализации интерфейса.

trait Foo[A] {
  def bar[B, C](a: A, b: B): C // метод, который принимает аргументы типа A и B и возвращает тип C
}

Конструкция if/else всегда возвращает значение выражения, которое стоит последним ввыполняемом блоке. Скобки вокруг условия обязательны, скобки вокруг тела, в котором только одна инструкция, можно опустить.

val a = if (true == false) {
  // true-branch
} else {
  // false-branch
}

Блок try/catch/finally выглядит в Scala так:

try {
  // some code
} catch {
  case e: SomeException => ???
  case e: SomeOtherException => ???
} finally {
  // some code
}

Циклы while ничем не отличаются от варианта в Java:

var i = 1000
while (i > 0) {
  i -= 1
}

А циклы for — наоборот, совсем не похожи (о них мы подробнее поговорим в следующей статье):

val numbers = 1 to 1000
for (number <- numbers) {
  print(number)
}

Также вы, возможно, могли заметить литерал ???. Он имеет тип Nothing который является подкласс любого класса. При вызове ??? кидает исключение NotImplemented. Это примерно аналог undefined в Python. ??? можно ставить в качестве заглушки вместо тела функции, к написанию которого вы планируете вернуться позже.

Заключение

Итак, мы установили и настроили среду разработки для Scala, посмотрели основные элементы языка, сходства с Java и отличия от нее. Этого вполне должно хватить для того, чтобы начать писать простой и рабочий код. В следующей статье мы подробнее рассмотрим элементы функционального программирования, case-классы, pattern-matching и другие высокоуровневые особенности языка.

Дополнительные материалы и ссылки

Официальный сайт
SBT
IntelliJ IDEA
ScalaTest

Scala is a general-purpose, high-level, multi-paradigm programming language. It is a pure object-oriented programming language which also provides support to the functional programming approach. Scala programs can convert to bytecodes and can run on the JVM(Java Virtual Machine). Scala stands for Scalable language. Scala doesn’t provide any support for .Net Framework. Scala was designed by the Martin Odersky, professor of programming methods at École Polytechnique Fédérale de Lausanne (EPFL) in Switzerland and a German computer scientist. Scala was first released publicly in 2004 on the Java platform as its first version. In June 2004. The latest version of scala is 2.12.6 which released on 27-Apr-2018. 
 

Features of Scala

Scala has many reasons for being popular and in demand. Few of the reasons are mentioned below:
 

  • Object- Oriented: Every value in Scala is an object so it is a purely object-oriented programming language. The behavior and type of objects are depicted by the classes and traits in Scala.
  • Functional: It is also a functional programming language as every function is a value and every value is an object. It provides the support for the high-order functions, nested functions, anonymous functions etc.
  • Statically Typed: The process of verifying and enforcing the constraints of types is done at compile time in Scala. Unlike other statically typed programming languages like C++, C etc., Scala doesn’t expect the redundant type of information from the user. In most cases, the user has no need to specify a type.
  • Extensible: New language constructs can be added to Scala in form of libraries. Scala is designed to interpolate with the JRE(Java Runtime Environment).
  • Concurrent & Synchronize Processing: Scala allows the user to write the codes in an immutable manner that makes it easy to apply the parallelism(Synchronize) and concurrency.

Application area

Getting Started with Scala

Scala is a very compatible language and thus can very easily be installed into the Windows and the Unix operating systems both very easily. 
Since Scala is a lot similar to other widely used languages syntactically, it is easier to code and learn in Scala. scala programs can be written on any plain text editor like notepad, notepad++, or anything of that sort. One can also use an online IDE for writing Scala codes or can even install one on their system to make it more feasible to write these codes because IDEs provide a lot of features like intuitive code editor, debugger, compiler, etc. 
To begin with, writing Scala Codes and performing various intriguing and useful operations, one must have scala installed on their system. This can be done by following the step by step instructions provided below: 
 

  1. Verifying Java Packages 
    The first thing we need to have is a Java Software Development Kit(SDK) installed on the computer. We need to verify this SDK packages and if not installed then install them.
  2. Now install Scala 
    We are done with installing the java now let’s install the scala packages. The best option to download these packages is to download from the official site only: https://www.scala-lang.org/download/ The packages in the link above is the approximately of 100MB storage. Once the packages are downloaded then open the downloaded .msi file.
  3. Testing and Running the Scala Commands 
    Open the command prompt now and type in the following codes. 
     
C:UsersYour_PC_username>scala
  1. We will receive an output as shown below: 
     

  1. Output of the command.

How to Run a Scala Program ?

Let’s consider a simple Hello World Program.
 

Scala

object Geeks {

def main(args: Array[String])

{

    println("Hello, World!")

}

Output: 
 

Hello, World!

Generally, there are two ways to Run a Scala program- 
 

  • Using Online IDEs: We can use various online IDEs which can be used to run Scala programs without installing.
  • Using Command-Line: We can also use command line options to run a Scala program. Below steps demonstrate how to run a Scala program on Command line in Windows/Unix Operating System: 

     Open Commandline and then to compile the code type scala Hello.scala. If your code has no error then it will execute properly and output will be displayed. 

Fundamentals of Scala

Variables

Variables are simply a storage location. Every variable is known by its name and stores some known and unknown piece of information known as value. In Scala there are two types of variable: 
 

  • Mutable Variables: These variables are those variables which allow us to change a value after the declaration of a variable. Mutable variables are defined by using the “var” keyword.
  • Immutable Variables: These variables are those variables which do not allow you to change a value after the declaration of a variable. Immutable variables are defined by using the “val” keyword.

Example: 
 

// Mutable Variable
var name: String = "geekforgeeks";

// Immutable Variable
val name: String = "geekforgeeks";

To know more about Scala Variables refer – Variables in Scala, Scope of Variable in Scala. 
 

Operators

An operator is a symbol that represents an operation to be performed with one or more operand. Operators allow us to perform different kinds of operations on operands. There are different types of operators used in Scala as follows: 
 

  • Arithmetic Operators
     
  • Relational Operators
  • Logical Operators
  • Assignment Operators
  • Bitwise Operators

Example : 
 

Scala

object Geeks

{

    def main(args: Array[String])

    {

        var a = 10;

        var b = 4;

        var c = true;

        var d = false;

        var result = 0;

        println ("Addition is: "+ (a + b) );

        println ("Subtraction is: "+ (a - b) ) ;

        if (a == b)

        {

           println ("Equal To Operator is True");

        

        else

        {

           println ("Equal To Operator is False");

        }

        println("Logical Or of a || b = " + (c || d)); 

        result = a & b;

        println ("Bitwise AND: " + result );

        println ("Addition Assignment Operator: " + (a += b) );

    }

}

Output:

Addition is: 14
Subtraction is: 6
Equal To Operator is False
Logical Or of a || b = true
Bitwise AND: 0
Addition Assignment Operator: ()

Decision Making

Decision Making in programming is similar to decision making in real life. Scala uses control statements to control the flow of execution of the program based on certain conditions. These are used to cause the flow of execution to advance and branch based on changes to the state of a program.
 

Decision Making Statements in Scala :

  • If
  • If – else
  • Nested – If
  • if – elsif ladder

Example 1: To illustrate use of if and if-else 
 

Scala

object Test

{

def main(args: Array[String])

{

    var a: Int = 650

    if(a % 2 == 0)

    {

        println("Even Number")

    }

    if (a > 698)

    {

        println("GeeksforGeeks")

    }

    else

    {

        println("Sudo Placement")

    }

}

}

Output:

Even Number
Sudo Placement

Example 2: To illustrate the use of Nested-if 
 

Scala

object Test

{

def main(args: Array[String])

{   

    var a: Int = 10;

    if(a % 2 == 0)

    

        if(a % 5 == 0)

        {  

            println("Number is divisible by 2 and 5n")  

        

    }

}

}

Output:

Number is divisible by 2 and 5

To know more about Decision Making please refer to Decision making in Scala
 

Loops

Looping in programming languages is a feature which facilitates the execution of a set of instructions/functions repeatedly while some condition evaluates to true. Loops make the programmer’s task simpler. Scala provides the different types of loop to handle the condition based situation in the program. The loops in Scala are : 
 

  • for loop 
     

Scala

object forloopDemo {

   def main(args: Array[String]) {

      var y = 0;

      for(y <- 1 to 4)

      {

         println("Value of y is: " + y);

      }

   }

}

  •  

Output:

Value of y is: 1
Value of y is: 2
Value of y is: 3
Value of y is: 4
  •  
  • While loop 
     

Scala

object whileLoopDemo

{

    def main(args: Array[String])

    {

        var x = 1;

        while (x <= 4)

        {

            println("Value of x: " + x);

            x = x + 1;

        }

    }

}

  •  

Output:

Value of x: 1
Value of x: 2
Value of x: 3
Value of x: 4
  •  
  • do-while loop 
     

Scala

object dowhileLoopDemo

{

    def main(args: Array[String])

    {

        var a = 10;

        do

        {

            print(a + " ");

            a = a - 1;

        }while(a > 0);

    }

}

  •  

Output:

10 9 8 7 6 5 4 3 2 1 
  •  

To know more about Loops please refer to Loops in Scala
 

Arrays

Array is a special kind of collection in scala. it is a fixed size data structure that stores elements of the same data type. It is a collection of mutable values. Below is the syntax. 
Syntax : 
 

var arrayname = new Array[datatype](size)

It will create an array of integers which contains the value 40, 55, 63, 17 and many more. Below is the syntax to access a single element of an array, if we’ve created an array named number.
 

number(0)

It will produce the output as 40. 
Iterating through an Array: 

In this example we create an array while providing the values of its elements at the same time. In this case, the type is inferred. 
 

Scala

object GFG

{

    def main(args: Array[String]) 

    {

        var name = Array("gfg", "geeks", "GeeksQuize"

                    "geeksforgeeks" )

        println("second element of an array is: ")

        println(name(1) )

    }

}

Output:

second element of an array is: 
geeks

To know more about arrays please refer to Arrays in Scala
 

Strings

A string is a sequence of characters. In Scala, objects of String are immutable which means a constant and cannot be changed once created. In Scala a String type is specified before meeting the string literal. but when the compiler meet to a string literal and creates a string object str. 
Syntax : 
 

var str = "Hello! GFG"
or
val str = "Hello! GFG"

var str: String = "Hello! GFG"
or
val str: String = "Hello! GFG"

Scala

object Main

{

    var str1 = "Hello! GFG"

    val str2: String = "GeeksforGeeks"

    def main(args: Array[String]) 

    {

        println(str1);

        println(str2);

    }

}

Output:

Hello! GFG
GeeksforGeeks

Concatenating strings in Scala:

when a new string is created by adding two strings is known as a concatenation of strings. Scala provides concat() method to concatenate two strings, this method returns a new string which is created using two strings. You can also use ‘+’ operator to concatenate two strings. 
 

Scala

object Main 

{

    var str1 = "Welcome! GeeksforGeeks "

    var str2 = " to Portal"

    def main(args: Array[String])

    {

        var Newstr = str1.concat(str2);

        println("String 1:" +str1);

        println("String 2:" +str2);

        println("New String :" +Newstr);

        println("This is the tutorial"

                    " of Scala language"

                    " on GFG portal");

    }

}

Output:

String 1:Welcome! GeeksforGeeks 
String 2: to Portal
New String :Welcome! GeeksforGeeks  to Portal
This is the tutorial of Scala language on GFG portal

To know more about Strings please refer to Strings in Scala
 

Functions

A function is a collection of statements that perform a certain task. Scala is assumed as functional programming language so these play an important role. It makes easier to debug and modify the code. Scala functions are first class values. Below is the syntax of Scala Functions. 
Syntax: 
 

def function_name ([parameter_list]) : [return_type] = {
   
  // function body

}

def keyword is used to declare a function in Scala.
Function Calling : There are mainly two ways to call the function in Scala. First way is the standard way as follows: 
 

function_name(paramter_list)

In the Second way, a user can also call the function with the help of the instance and dot notation as follows: 
 

[instance].function_name(paramter_list)

Scala

object GeeksforGeeks {

   def main(args: Array[String]) {

      println("Sum is: " + functionToAdd(5, 3));

   }

   def functionToAdd(a:Int, b:Int) : Int =

   {

       var sum:Int = 0

       sum = a + b

       return sum

   }

}

Output : 
 

Sum is: 8

 
Anonymous Functions in Scala : 
In Scala, An anonymous function is also known as a function literal. A function which does not contain a name is known as an anonymous function.
Syntax : 
 

(z:Int, y:Int)=> z*y
Or
(_:Int)*(_Int)

Scala

object Main 

{

    def main(args: Array[String]) 

    {

        var myfc1 = (str1:String, str2:String) => str1 + str2

        var myfc2 = (_:String) + (_:String)

        println(myfc1("Geeks", "12Geeks"))

        println(myfc2("Geeks", "forGeeks"))

    }

}

Output : 
 

Geeks12Geeks
GeeksforGeeks

  
Scala Nested Functions: 
A function definition inside an another function is known as Nested Function. In Scala, we can define functions inside a function and functions defined inside other functions are called nested or local functions.
Syntax : 
 

def FunctionName1( perameter1, peramete2, ..) = {
def FunctionName2() = {
// code
}
}

Scala

object MaxAndMin 

{

    def main(args: Array[String]) 

    {

        println("Min and Max from 5, 7")

                maxAndMin(5, 7);

    }

    def maxAndMin(a: Int, b: Int) = {

       def maxValue() =

          if(a > b) 

          {

              println("Max is: " + a)

          }

          else

          {

              println("Max is: " + b)

          }

       }

       def minValue() = {

          if (a < b)

          {

              println("Min is: " + a)

          }

          else

          {

              println("Min is: " + b)

          }

       }

       maxValue();

       minValue();

    }

}

Output: 
 

Min and Max from 5, 7
Max is: 7
Min is: 5

 
Currying Functions in Scala : 
Currying in Scala is simply a technique or a process of transforming a function. This function takes multiple arguments into a function that takes single argument.
Syntax : 
 

def function name(argument1, argument2) = operation

Scala

object Curry

{

    def add(x: Int, y: Int) = x + y;

    def main(args: Array[String])

    {

        println(add(20, 19));

    }

}

Output: 
 

39

Object Oriented Programming

Object-oriented programming aims to implement real-world entities like inheritance, hiding, polymorphism, etc in programming. The main aim of OOP is to bind together the data and the functions that operate on them so that no other part of the code can access this data except that function. 
OOPs Concepts: 
 

  • Polymorphism
  • Inheritance 
     
  • Abstract-Classes
  • Class and Object
  • Constructors

OOPs-Concepts-In-Perl

Creation of a Class and an Object: 
Classes and Objects are basic concepts of Object Oriented Programming which revolve around the real-life entities. A class is a user-defined blueprint or prototype from which objects are created. 
 

Scala

class Smartphone

{

    var number: Int = 16

    var nameofcompany: String = "Apple"

    def Display()

    {

        println("Name of the company : " + nameofcompany);

        println("Total number of Smartphone generation: " + number);

    }

}

object Main 

{

    def main(args: Array[String]) 

    {

        var obj = new Smartphone();

        obj.Display();

    }

}

Output:

Name of the company : Apple
Total number of Smartphone generation: 16

Traits

Traits are like interfaces in Java. But they are more powerful than the interface in Java because in the traits you are allowed to implement the members. Traits can have methods(both abstract and non-abstract), and fields as its members.
Creating a trait – 
 

Scala

trait MyTrait

{

    def pet 

    def pet_color

}

class MyClass extends MyTrait

{

    def pet()

    {

        println("Pet: Dog")

    }

    def pet_color()

    {

        println("Pet_color: White")

    }

    def pet_name()

    {

        println("Pet_name: Dollar")

    }

object Main 

{

    def main(args: Array[String]) 

    {

        val obj = new MyClass();

        obj.pet();

        obj.pet_color();

        obj.pet_name();

    }

}

Output : 
 

Pet: Dog
Pet_color: White
Pet_name: Dollar

To know more about Traits please refer to Traits in Scala
 

Regular Expression

Regular Expressions explain a common pattern utilized to match a series of input data so, it is helpful in Pattern Matching in numerous programming languages. In Scala Regular Expressions are generally termed as Scala Regex. 
 

Scala

object GfG

{

    def main(args

             : Array[String])

    {

        val portal = "GeeksforGeeks".r

                         val CS

            = "GeeksforGeeks is a CS portal."

            println(portal findFirstIn CS)

    }

}

Output : 
 

Some(GeeksforGeeks)

To know more about tuple please refer to Regular Expressions in Scala.
 

Exceptions

An exception is an unwanted or unexpected event, which occurs during the execution of a program i.e at run time. These events change the flow control of the program in execution.
 

Exception Hierarchy

In scala, All exceptions are unchecked. there is no concept of checked exception Scala facilitates a great deal of flexibility in terms of the ability to choose whether to catch an exception.
The Throwing Exceptions : 
 

Scala

object Main 

{

    def validate(article:Int)=

    {  

        if(article < 20)  

            throw new ArithmeticException("You are not eligible for internship")  

        else println("You are eligible for internship")  

    }  

    def main(args: Array[String])

    {

            validate(22)

    }

}

Output : 
 

You are eligible for internship

Try-Catch Exceptions : 
 

Scala

import java.io.IOException

object GFG

{  

    def main(args:Array[String])

    {  

        try

        {

            var N = 5/0

        

        catch

        {

            case i: IOException =>

            {

                println("IOException occurred.")

            }

            case a : ArithmeticException =>

            {

                println("Arithmetic Exception occurred.")

            }

        }

    }  

}  

Output : 
 

Arithmetic Exception occurred.

File Handling

File Handling is a way to store the fetched information in a file. Scala provides packages from which we can create, open, read and write the files. For writing to a file in scala we borrow java.io._ from Java because we don’t have a class to write into a file, in the Scala standard library. We could also import java.io.File and java.io.PrintWriter.
 

  • Creating a new file : 
    1. java.io.File defines classes and interfaces for the JVM access files, file systems and attributes.
    2. File(String pathname) converts theparameter string to abstract path name, creating a new file instance.
  • Writing to the file : 
    java.io.PrintWriter includes all the printing methods included in PrintStream. 
    Below is the implementation for creating a new file and writing into it. 
     

Scala

import java.io.File

import java.io.PrintWriter

object Geeks

{

    def main(args:Array[String])

    {

        val file_Object = new File("abc.txt"

        val print_Writer = new PrintWriter(file_Object) 

        print_Writer.write("Hello, This is Geeks For Geeks"

        print_Writer.close()       

}

}     

  •  

  • Reading a File : 
    Below is the example to reading a file. 
     

Scala

import scala.io.Source

object GeeksScala

{

    def main(args : Array[String])

    {

        val fname = "abc.txt"

        val fSource = Source.fromFile(fname) 

        while (fSource.hasNext)

        {

            println(fSource.next)

        }

        fSource.close() 

    }

}

  •  

To know more about various different File Handling, please refer to File Handling in Scala
 

Scala Collection

List

A list is a collection which contains immutable data. List represents linked list in Scala. The Scala List class holds a sequenced, linear list of items. Lists are immutable whereas arrays are mutable in Scala. In a Scala list, each element must be of the same type. list is defined under scala.collection.immutable package. 
Syntax : 
 

val variable_name: List[type] = List(item1, item2, item3)
or
val variable_name = List(item1, item2, item3)

Create and initialize Scala List 
Example : 
 

Scala

import scala.collection.immutable._

object GFG

{

    def main(args:Array[String])

    {  

        val mylist1: List[String] = List("Geeks", "GFG",

                            "GeeksforGeeks", "Geek123")

        val mylist2 = List("C", "C#", "Java", "Scala",

                                        "PHP", "Ruby")

        println("List 1:")

        println(mylist1)

        println("nList 2:")

        for(mylist<-mylist2)

        {

            println(mylist)

        }

    }

}

Output : 
 

List 1:
List(Geeks, GFG, GeeksforGeeks, Geek123)

List 2:
C
C#
Java
Scala
PHP
Ruby

To know more about List please refer to List in Scala.
 

Map

Map is a collection of key-value pairs. In other words, it is similar to dictionary. Keys are always unique while values need not be unique. In order to use mutable Map, we must import scala.collection.mutable.Map class explicitly. 
Creating a map and accessing the value 
Example : 
 

Scala

object GFG

{

    def main(args:Array[String])

    {

        val mapIm = Map("Ajay" -> 30

                        "Bhavesh" -> 20,

                        "Charlie" -> 50)

        val ajay = mapIm("Ajay"

        println(ajay)

    }

Output : 
 

30

To know more about Map please refer to Map in Scala.
 

Iterator

An iterator is a way to access elements of a collection one-by-one. It resembles to a collection in terms of syntax but works differently in terms of functionality. To access elements we can make use of hasNext() to check if there are elements available and next() to print the next element. 
Syntax: 
 

val v = Iterator(5, 1, 2, 3, 6, 4)

//checking for availability of next element
while(v.hasNext)

//printing the element
println(v.next)

Example : 
 

Scala

object GFG

{

    def main(args:Array[String])

    {

        val v = Array(5, 1, 2, 3, 6, 4)

        val i = v.iterator

        while (i.hasNext)

            print(i.next + " ")

    }

}

Output: 
 

5 1 2 3 6 4 

To know more about tuple please refer to Iterators in Scala.
 

Set

A set is a collection which only contains unique items. The uniqueness of a set are defined by the == method of the type that set holds. If you try to add a duplicate item in the set, then set quietly discard your request. 
Syntax : 
 

// Immutable set
val variable_name: Set[type] = Set(item1, item2, item3)
or
val variable_name = Set(item1, item2, item3)

// Mutable Set
var variable_name: Set[type] = Set(item1, item2, item3)
or
var variable_name = Set(item1, item2, item3)

Creating and initializing Immutable set 
Example : 
 

Scala

import scala.collection.immutable._

object Main 

{

    def main(args: Array[String]) 

    {

        val myset1: Set[String] = Set("Geeks", "GFG"

                            "GeeksforGeeks", "Geek123")

        val myset2 = Set("C", "C#", "Java", "Scala"

                                          "PHP", "Ruby")

        println("Set 1:")

        println(myset1)

        println("nSet 2:")

        for(myset<-myset2)

        {

            println(myset)

        }

    }

}

Output : 
 

Set 1:
Set(Geeks, GFG, GeeksforGeeks, Geek123)

Set 2:
Scala
C#
Ruby
PHP
C
Java

Creating and initializing mutable set 
Example : 
 

Scala

import scala.collection.immutable._

object Main 

{

    def main(args: Array[String])

    {

        var myset1: Set[String] = Set("Geeks", "GFG"

                            "GeeksforGeeks", "Geek123")

        var myset2 = Set(10, 100, 1000, 10000, 100000)

        println("Set 1:")

        println(myset1)

        println("nSet 2:")

        myset2.foreach((item:Int)=>println(item))

    }

}

Output : 
 

Set 1:
Set(Geeks, GFG, GeeksforGeeks, Geek123)

Set 2:
10
100000
10000
1000
100

To know more about Set please refer to Set in Scala | Set-1 and Set in Scala | Set-2.
 

Tuple

Tuple is a collection of elements. Tuples are heterogeneous data structures, i.e., is they can store elements of different data types. A tuple is immutable, unlike an array in scala which is mutable. 
Creating a tuple and accessing an element 
Example : 
 

Scala

object gfg 

{

    def main(args: Array[String]) 

    {

        var name = (15, "chandan", true)

        println(name._1)

        println(name._2)

        println(name._3)

    }

}

Output : 
 

15
chandan
true

To know more about tuple please refer to tuple in Scala.
 

Внутри объекта Hello мы пишем одну функцию println (), которая используется для печати текста внутри него на консоли. Мы запустим наш код, нажав на зеленую стрелку.

Нажатие на стрелку предоставляет нам опцию Run, hello, после нажатия на нее наш код начнет компилироваться, и через несколько секунд мы увидим результаты нашей программы, напечатанные из консоли, встроенной в IntelliJ IDE.

И вот, мы успешно установили Scala и запустили нашу первую программу.

Язык Scala имеет анонимные функции, которые также называются функциональными литералами . Scala, являющийся функциональным языком, часто означает, что разработчики разбивают большие проблемы на множество мелких задач и создают множество функций для решения этих проблем. Чтобы упростить создание функций, Scala содержит эти функции, которые могут быть созданы без имени . Мы можем назначить их непосредственно переменным или определениям «def» следующим образом

val multiplyByTwo = (n:Int) => n * 2
def multiplyByThree = (n:Int) => n *3

Затем мы можем использовать обычные способы использования функций, передавая им параметры следующим образом.

multiplyByTwo(3)

//6

multiplyByThree(4)

//12

Эти методы пригодятся, когда мы хотим иметь чистый и лаконичный код. Мы можем использовать анонимные функции при определении методов, которые не являются большими и не требуют большого количества кода в их теле. Они очень простые и не нуждаются в церемонии для создания.

Эти методы не ограничиваются функциями с аргументами и могут использоваться для создания экземпляров методов, которые не принимают никаких аргументов.

val sayHello = ()=>{ println("hello") }

Большинство из этих анонимных функций используются в других частях нашего кода, где нам нужно создать быструю функцию на месте.

Другая причина, почему эти функции также называются встроенными функциями . Использование анонимных функций является распространенным шаблоном, который широко используется в библиотеке коллекций для выполнения быстрых действий над коллекцией.

Например, у нас есть метод filter, который использует встроенную функцию / анонимную функцию для создания другой коллекции только с элементами, которые соответствуют критериям, которые мы определили в анонимной функции.

val myList = List(1,2,3,4,5,6,7)

val myEvenList = myList.filter((n: Int) => n % 2 == 0)
//List(2,4,6)

val myOddList = myList.filter((n:Int) => n % 2 != 0)
//List(1,3,5,7)

Здесь у нас есть анонимные функции, которые проверяют, является ли значение, которое мы получаем из списка, нечетным и четным, и возвращают элемент.

//the one checking that the value is even
(n: Int) => n % 2 == 0

//the one checking that the value is odd
(n:Int) => n % 2 != 0

В Scala также можно использовать подстановочные знаки, где параметры нашей анонимной функции не названы. Например

var timesTwo = (_:Int)*2

timesTwo(5)
//10

В этом сценарии мы не называем передаваемый параметр. Единственное, что мы используем для подчеркивания, это подчеркивание.

Большинство языков последовательно оценивают переменные и параметры функции, один за другим. В Scala у нас есть ключевое слово lazy, которое помогает работать со значениями, которые мы не хотим оценивать, пока на них нет ссылок.

Переменная, помеченная как ленивая, не будет оцениваться там, где она определена, это обычно называется нетерпеливым вычислением, она будет оцениваться только тогда, когда на нее ссылаются позже в коде.

Это может быть полезно, когда оценка значения может быть дорогостоящим вычислением, если это не тот случай, когда значение всегда необходимо, мы можем уберечь себя от выполнения дорогостоящих вычислений, которые могут замедлить наше программное обеспечение, сделав нашу переменную ленивой.

lazy val myExpensiveValue = expensiveComputation

def runMethod()={
    if(settings == true){
        use(myExpensiveValue)
    }else{
        use(otherValue)
    }
}

Это не единственный случай использования ленивых переменных. Они также помогают решать проблемы циклической зависимости в коде.

В случае ложных настроек нам может не потребоваться использовать myExoyValue, что может привести к тому, что мы избавим нас от дорогостоящих вычислений, которые помогут гарантировать, что пользователи хорошо проводят время, используя наше приложение, поскольку наши другие потребности могут быть правильно рассчитаны без чрезмерных затрат. баран.

В случае ложных настроек нам может не потребоваться использовать myExoyValue, что может привести к тому, что мы избавим нас от дорогостоящих вычислений, которые помогут обеспечить приятное времяпрепровождение пользователям при использовании нашего приложения, так как другие их потребности могут быть вычислены соответствующим образом без чрезмерных затрат. баран.

Лень также помогает с аргументами функции, где аргументы используются только тогда, когда на них ссылаются внутри функции. Эта концепция называется параметрами Call-by-name.

def sometimesUsedString(someValue:String, defaultValue:=> String)={
 if(someValue != null){
   use(defaultValue)
 }else{
   use(someValue)
   }
 }

Многие языки используют способ оценки аргументов по значению. Параметр, переданный через call-by-name, будет оцениваться только при необходимости в теле функции и не будет оцениваться до этого. Как только значение оценено, оно сохраняется и может быть повторно использовано позже без необходимости повторной оценки. Концепция, которая известна как запоминание.

Функция более высокого порядка – это функция, которая может принимать функции в качестве аргументов и может возвращать функцию в качестве возвращаемого типа. В Scala функции считаются гражданами первого класса. Такое использование этих функций позволяет нам быть очень гибкими в отношении программ, которые мы можем создавать. Мы можем динамически создавать функции и динамически передавать функциональность другим функциям.

def doMathToInt(n:Int, myMathFunction:Int=>Int): Int ={
    myMathFunction(n)
}

В приведенной выше функции мы передаем int и функцию, которая принимает int и возвращает int. Мы можем передать любую функцию этой подписи. Под подписью мы подразумеваем ввод и вывод функции. Подпись Int => Int означает, что функция принимает Int в качестве входных данных и возвращает Int в качестве своих выходных.

Подпись () => Int означает, что функция ничего не принимает в качестве входных данных и возвращает Int в качестве выходных. Примером такой функции может быть случайная функция int для нас.

def generateRandomInt()={
 return scala.util.Random.nextInt()
}

Вышеуказанная функция имеет сигнатуру () => Int

У нас может быть функция с подписью scala () => Unit. Это означает, что функции ничего не принимают и не возвращают тип. Функция может выполнять какие-то вычисления, изменяя что-то, делая что-то предопределенное.

Подобные методы не приветствуются, так как они кажутся черным ящиком, который может повлиять на систему некоторыми неизвестными способами. Они также непроверены. Наличие явных типов ввода и вывода позволяет нам рассуждать о том, что делает наша функция.

Функция высшего порядка также может возвращать функцию.

Например, мы могли бы создать метод, который создаст функцию включения, то есть берет число и применяет к нему мощность.

def powerByFunction(n:Int):Int=>Int = {
  return (x:Int)=> scala.math.pow(x,n).toInt
}

Вышеуказанная функция принимает int. Наш возвращаемый тип – анонимная функция, которая принимает Int x, * мы используем int x в качестве аргумента для степенной функции.

В Scala мы можем преобразовать функцию, которая принимает два аргумента, в один, который принимает один аргумент за раз. Когда мы передаем один аргумент, мы частично применяем его и в результате получаем функцию, которая принимает один аргумент для завершения функции. Карринг позволяет нам создавать функции, частично добавляя некоторые аргументы.

Это может быть полезно для динамического создания функций, прежде чем мы получим полный набор аргументов

def multiply two numbers(n:Int)(m:Int): Unit ={
  return n * m
}

Если нам нужно создать функцию, которая умножается на определенное число, нам не нужно создавать другой метод умножения.

Мы можем просто вызвать .curried для нашей функции выше и получить функцию, которая сначала принимает один аргумент, и вернуть частично примененную функцию

def multiplyTwoNumbers(n:Int)(m:Int): Unit ={
  return n * m
}

var multiplyByFive = multiplyTwoNumbers(5) 

multiplyByFive(4)

//returns 20

В Scala есть мощный встроенный механизм, помогающий нам проверить, соответствует ли переменная определенным критериям, так же, как мы это делали бы в операторе switch в Java или в ряде операторов if / else. В языке есть сопоставление с образцом, которое мы можем использовать, чтобы проверить, относится ли переменная к определенному типу. Сопоставление с образцом в Scala является мощным средством и может использоваться для деструктуризации компонентов, которые имеют неприменимый метод, чтобы получить интересующие нас поля непосредственно из переменной, которую мы сопоставляем.

Сопоставление с образцом в Scala также обеспечивает более приятный синтаксис по сравнению с оператором switch.

myItem match {
  case true => //do something
  case false => //do something else
  case  _ => //if none of the above do this by default
}

Мы сравниваем нашу переменную с набором параметров, и когда соответствующая переменная соответствует критериям, выражение в правой части жирной стрелки (=>) вычисляется и возвращается как результат сопоставления.

Мы используем подчеркивание для отлова случаев, которые не встречаются в нашем коде. Он отражает поведение случая по умолчанию при работе с операторами switch.

class Animal(var legs:Int,var sound:String)
class Furniture(var legs:Int, var color:Int, var woodType:String)

myItem match {
case myItem:Animal => //do something
case myItem:Furniture => //do something else
case _ => //case we have a type we don't recognize do sth else
}

В приведенном выше коде вы можете узнать тип переменной myItem и на основе этой ветви перейти к некоторому конкретному коду.

Сопоставление с образцом проверяет, соответствует ли переменная

Подчеркивание работает как заполнитель, который соответствует любому другому условию, которое не соответствует другим элементам в описаниях выше. Мы берем переменную myItem и вызываем метод match.

  • мы проверяем, является ли myItem истинным с помощью и делаем некоторую логику в правой части жирной стрелки “=>.”
  • мы используем подчеркивание, чтобы соответствовать чему-либо, что не соответствует ни одному из операторов case, которые мы определили в коде.

С классами Case мы можем даже пойти дальше и деструктурировать класс, чтобы получить поля внутри объекта.

Используя ключевое слово sealed для определения наших классов, мы получаем преимущество, заключающееся в том, что компилятор тщательно проверяет случаи, с которыми мы пытаемся сопоставить, и предупреждает нас, если мы забываем обработать конкретный.

Можно создать значения, которые не могут быть изменены другими функциями в Scala, используя ключевое слово val. Это достигается в Java с помощью ключевого слова final. В Scala мы используем ключевое слово val при создании переменной вместо var, что является альтернативой, которую мы использовали бы для создания изменяемой переменной.

Переменная, определенная с использованием ключевого слова val, доступна только для чтения, тогда как переменная, определенная с помощью var, может быть прочитана и изменена другими функциями или произвольно пользователем в коде.

var changeableVariable = 8

changeableVariable =10
//the compiler doesn't complain, and the code compiles successfully

println(changeableVariable)
//10

val myNumber = 7

myNumber = 4

//if we try this the code won't compile

Попытка присвоить значение myNumber после того, как мы объявили его как val, выдает ошибку времени компиляции или «переназначение в val».

Зачем использовать неизменность?

Неизменность помогает нам предотвратить непредвиденное изменение кода и других программистов нашими значениями, что может привести к неожиданным результатам, если они намерены использовать хранимое нами значение, вместо этого они могут сделать его копию. Таким образом, ошибки, которые могут быть вызваны несколькими действующими лицами, меняющими одну и ту же переменную, предотвращаются.

Все мы знаем, что объекты – это объекты реального мира, а класс – это шаблон, который определяет объекты. Классы имеют как состояние, так и поведение. Состояния являются значениями или переменными. Поведение – это методы в Scala.

Давайте посмотрим, как вы можете определить класс, создать его экземпляр и использовать его с помощью Scala.

Здесь класс называется Rectangle, который имеет две переменные и две функции. Вы также можете использовать параметры l и b непосредственно как поля в программе. У вас есть объект, который имеет метод main и создал экземпляр класса с двумя значениями.

Пример:

class Rectangle( l: Int,  b: Int) {
  val length: Int = l
  val breadth: Int = b
  def getArea: Int = l * b
  override def toString = s"This is rectangle with length as $length and breadth as  $breadth"
  }
object RectObject {
  def main(args: Array[String]) {
    val rect = new Rectangle(4, 5)
    println(rect.toString)
    println(rect.getArea)    
  }
}

Все поля и метод по умолчанию общедоступны в Scala. Важно использовать переопределение, потому что метод toString определен для объекта в Scala.

Scala has multiple types of inheritance (like single, multi-level, multiple, hierarchical, hybrid,) that share a lot in common with traditional forms found in Java. You can inherit both from classes and traits. You can inherit the members of one class into another class using the keyword “extends”. This enables reusability.

It’s possible to inherit from one class or multiple classes. It’s also possible to inherit from subclasses that themselves have their superclasses, creating a hierarchy of inheritance in the process.

In the below example, the Base class is Circle, and derived class is Sphere. A circle has a value called radius, which is inherited in the Sphere class. The method calcArea is overridden using the keyword override.

Example:

class Circle {
  val radius = 5;
  def calcArea = {
    println(radius * radius )
  }
}
class Sphere extends Circle{
 override def calcArea = {
    println(radius * radius * radius )
  }
}
  object SphereObject{
    def main(args : Array[String]){
      new Sphere().calcArea 
    }
  }

In Scala, we can create abstract methods and member fields using abstract classes and traits. Inside abstract classes and traits, we can define abstract fields without necessarily implementing them.

Example:

trait MakesSound{
    var nameOfSound:String
    def sound():String
}
abstract class HasLegs(var legs:Int){
    val creatureName:String

    def printLegs():String={
        return s"$creatureName has this number of legs: $legs"
    }
}

These fields are implemented by the classes that extend the trait or abstract class. You can use traits to create contracts as to what our application should be able to do and then implement those methods later.

trait DatabaseService{
    def addItemName(itemName:String)
    def removeItem(itemId:Int)
    def updateItem(itemId:Int, newItemName:String)
}

This way, we can plan out how our application will look like without implementing the methods which can help us envision how various methods will look like. It follows along a pattern know as programming to abstractions and not the actual implementation.

The class which is prefaced with keyword abstract can contain both abstract and non-abstract methods. But, multiple inheritances is not supported in abstract class. So, you can extend at most one abstract class.

A Singleton is a class that is only instantiated once in a program. It’s from a popular and useful programming pattern known as the “singleton pattern”. It’s useful in creating instances that are meant to be long-living and will be commonly accessed throughout your program whose state is integral in coordinating the events of a system. Creating such a class in Scala is easy as Scala provides us with a simple means of creating singletons using the object keyword.

object UserProfile{
    var userName=""
    var isLoggedIn:Boolean = false
}

We can then reference this object throughout our program with the guarantee that all parts of our program will see the same data as there’s only one instance of it.

def getLoggedInStatus():Boolean={
   return UserProfile.isLoggedIn
}

def changeLoggedInStatus():Boolean={
    UserProfile.isLoggedIn = !UserProfile.isLoggedIn
    return  UserProfile.isLoggedIn
}

В Scala отсутствует понятие статических членов, поэтому вам нужно использовать одноэлементные объекты, которые действуют как статические члены класса.

Неявные классы – это новая функциональность, добавленная после версии 2.1. Это в первую очередь, чтобы добавить новую функциональность для закрытых классов.

Неявное ключевое слово должно быть определено в классе, объекте или признаке. Первичный конструктор неявного класса должен иметь ровно один аргумент в своем первом списке параметров. Он также может включать дополнительный список неявных параметров.

В приведенном ниже примере добавлена ​​новая функциональность для замены гласных строки на *.

object StringUtil {
  implicit class StringEnhancer(str: String) {
    
    def replaceVowelWithStar: String = str.replaceAll("[aeiou]", "*")
  }
}

Вам нужно импортировать в тот класс, где вы его используете.

import StringUtil.StringEnhancer

object ImplicitEx extends App {
  val msg = "This is Guru99!"
  println(msg.replaceVowelWithStar)
}

В ООП программы создаются путем группировки данных и функций, которые оперируют этими данными, в сильно связанные блоки. Объекты переносят свои данные в те поля и методы, которые с ними работают. В этом стиле программирования основной абстракцией являются данные, поскольку создаваемые методы предназначены для работы с данными.

Функциональное программирование, с другой стороны, разделяет данные и функции, которые работают с данными. Это позволяет разработчикам рассматривать функции как абстракцию и движущую силу при моделировании программ.

Scala обеспечивает функциональное программирование, имея функции в качестве первоклассных граждан, позволяя передавать их как значения другим функциям и возвращать как значения. Сочетание этих двух парадигм сделало Scala отличным выбором для создания сложного программного обеспечения в различных отраслях, таких как Data Science.

  • Programming

Введение в Scala

1. Вступление

В этом руководстве мы рассмотримScala — один из основных языков, работающих на виртуальной машине Java.

Мы начнем с основных функций языка, таких как значения, переменные, методы и управляющие структуры. Затем мы рассмотрим некоторые расширенные функции, такие как функции высшего порядка, каррирование, классы, объекты и сопоставление с образцом.

2. Настройка проекта

Во-первых, давайте добавим зависимость scala-library к нашему pom.xml. Этот артефакт предоставляет стандартную библиотеку для языка:


    org.scala-lang
    scala-library
    2.12.7

Во-вторых, давайте добавимscala-maven-plugin для компиляции, тестирования, запуска и документирования кода:


    net.alchim31.maven
    scala-maven-plugin
    3.3.2
    
        
            
                compile
                testCompile
            
        
    

Наконец, мы будем использовать JUnit для модульного тестирования.

3. Основные характеристики

В этом разделе мы рассмотрим основные языковые функции на примерах. Для этого мы будем использоватьScala interpreter.

3.1. переводчик

Интерпретатор представляет собой интерактивную оболочку для написания программ и выражений.

Давайте с его помощью напечатаем «hello world»:

C:>scala
Welcome to Scala 2.12.6 (Java HotSpot(TM)
 64-Bit Server VM, Java 1.8.0_92).
Type in expressions for evaluation.
Or try :help.

scala> print("Hello World!")
Hello World!
scala>

Выше мы запускаем интерпретатор, набирая «scala» в командной строке. Переводчик запускается и отображает приветственное сообщение, за которым следует подсказка.

Затем мы набираем наше выражение в этой подсказке. Интерпретатор читает выражение, оценивает его и печатает результат. Затем он зацикливается и снова отображает подсказку.

Поскольку он обеспечивает немедленную обратную связь, переводчик является самым простым способом начать работу с языком. Поэтому давайте с его помощью исследуем основные особенности языка: выражения и различные определения.

3.2. Выражения

Any computable statement is an expression.

Напишем несколько выражений и посмотрим их результаты:

scala> 123 + 321
res0: Int = 444

scala> 7 * 6
res1: Int = 42

scala> "Hello, " + "World"
res2: String = Hello, World

scala> "zipZAP" * 3
res3: String = zipZAPzipZAPzipZAP

scala> if (11 % 2 == 0) "even" else "odd"
res4: String = odd

Как мы видим выше,every expression has a value and a type.

If an expression does not have anything to return, it returns a value of type Unit. Этот тип имеет только одно значение:(). Он похож на ключевое словоvoid в Java.

3.3. Определение значения

Ключевое словоval используется для объявления значений.

Мы используем его, чтобы назвать результат выражения:

scala> val pi:Double = 3.14
pi: Double = 3.14

scala> print(pi)
3.14

Это позволяет нам многократно использовать результат.

Values are immutable. Поэтому мы не можем переназначить их:

scala> pi = 3.1415
:12: error: reassignment to val
       pi = 3.1415
         ^

3.4. Определение переменной

Если нам нужно переназначить значение, мы объявим его как переменную.

Ключевое словоvar  используется для объявления переменных:

scala> var radius:Int=3
radius: Int = 3

3.5. Определение метода

Мы определяем методы с помощью ключевого словаdef. После ключевого слова мы указываем имя метода, объявления параметров, разделитель (двоеточие) и тип возвращаемого значения. После этого мы указываем разделитель (=), за которым следует тело метода.

В отличие от Java, мы не используем ключевое словоreturn для возврата результата. Метод возвращает значение последнего вычисленного выражения.

Напишем методavg для вычисления среднего двух чисел:

scala> def avg(x:Double, y:Double):Double = {
  (x + y) / 2
}
avg: (x: Double, y: Double)Double

Затем вызовем этот метод:

scala> avg(10,20)
res0: Double = 12.5

Если метод не принимает никаких параметров, мы можем опустить круглые скобки во время определения и вызова. Кроме того, мы можем опустить фигурные скобки, если в теле есть только одно выражение.

Давайте напишем метод без параметровcoinToss w, который случайным образом возвращает «Head» или «Tail»:

scala> def coinToss =  if (Math.random > 0.5) "Head" else "Tail"
coinToss: String

Затем давайте вызовем этот метод:

scala> println(coinToss)
Tail
scala> println(coinToss)
Head

4. Управляющие структуры

Управляющие структуры позволяют нам изменять поток управления в программе. У нас есть следующие структуры управления:

  • Выражение if-else

  • Пока цикл и сделать пока цикл

  • Для выражения

  • Попробуй выражение

  • Выражение соответствия

В отличие от Java, у нас нет ключевых словcontinue илиbreak. У нас есть ключевое словоreturn. Тем не менее, мы должны избегать его использования.

Вместо оператораswitch у нас есть сопоставление с образцом через выражение сопоставления. Кроме того, мы можем определить наши собственные абстракции управления.

4.1. если еще

Секспрессияif-else аналогична Java. Частьelse не является обязательной. Мы можем вкладывать несколько выражений if-else.

Since it is an expression, it returns a value. Поэтому мы используем его аналогично троичному оператору (? :) в Java. Фактически, языкdoes not have have the ternary operator.

Используя if-else, давайте напишем метод для вычисления наибольшего общего делителя:

def gcd(x: Int, y: Int): Int = {
  if (y == 0) x else gcd(y, x % y)
}

Затем давайте напишем модульный тест для этого метода:

@Test
def whenGcdCalledWith15and27_then3 = {
  assertEquals(3, gcd(15, 27))
}

4.2. Пока петля

Цикл while имеет условие и тело. Он многократно оценивает тело в цикле, пока условие истинно — условие оценивается в начале каждой итерации.

Поскольку ничего полезного не возвращается, он возвращаетUnit.

Давайте воспользуемся циклом while, чтобы написать метод вычисления наибольшего общего делителя:

def gcdIter(x: Int, y: Int): Int = {
  var a = x
  var b = y
  while (b > 0) {
    a = a % b
    val t = a
    a = b
    b = t
  }
  a
}

Затем проверим результат:

assertEquals(3, gcdIter(15, 27))

4.3. Делать во время цикла

Цикл do while похож на цикл while, за исключением того, что условие цикла вычисляется в конце цикла.

Используя цикл do-while, напишем метод вычисления факториала:

def factorial(a: Int): Int = {
  var result = 1
  var i = 1
  do {
    result *= i
    i = i + 1
  } while (i <= a)
  result
}

Далее проверим результат:

assertEquals(720, factorial(6))

4.4. Для выражения

Выражение for гораздо более универсально, чем цикл for в Java.

Он может перебирать одну или несколько коллекций. Более того, он может фильтровать элементы, а также создавать новые коллекции.

Используя выражение for, напишем метод для суммирования диапазона целых чисел:

def rangeSum(a: Int, b: Int) = {
  var sum = 0
  for (i <- a to b) {
    sum += i
  }
  sum
}

Здесьa to b — выражение генератора. Он генерирует серию значений отa доb.

i ← a to b  — генератор. Он определяетasval и присваивает ему серию значений, созданных выражением генератора.

Тело выполняется для каждого значения в серии.

Далее проверим результат:

assertEquals(55, rangeSum(1, 10))

5. функции

Скала — это функциональный язык. Здесь функции являются первоклассными значениями — мы можем использовать их как любой другой тип значения.

В этом разделе мы рассмотрим некоторые сложные концепции, связанные с функциями — локальные функции, функции высшего порядка, анонимные функции и каррирование.

5.1. Локальные функции

We can define functions inside functions. Они называются вложенными функциями или локальными функциями. Аналогично локальным переменнымthey are visible only within the function they are defined in.

Теперь давайте напишем метод вычисления мощности с помощью вложенной функции:

def power(x: Int, y:Int): Int = {
  def powNested(i: Int,
                accumulator: Int): Int = {
    if (i <= 0) accumulator
    else powNested(i - 1, x * accumulator)
  }
  powNested(y, 1)
}

Далее проверим результат:

assertEquals(8, power(2, 3))

5.2. Функции высшего порядка

Поскольку функции являются значениями, мы можем передать их в качестве параметров другой функции. У нас также может быть функция, возвращающая другую функцию.

We refer to functions which operate on functions as higher-order functions. Они позволяют нам работать на более абстрактном уровне. Используя их, мы можем уменьшить дублирование кода, написав обобщенные алгоритмы.

Теперь давайте напишем функцию высшего порядка для выполнения сопоставления и сокращения операций над диапазоном целых чисел:

def mapReduce(r: (Int, Int) => Int,
              i: Int,
              m: Int => Int,
              a: Int, b: Int) = {
  def iter(a: Int, result: Int): Int = {
    if (a > b) {
      result
    } else {
      iter(a + 1, r(m(a), result))
    }
  }
  iter(a, i)
}

Здесьr иm — параметры типаFunction. Передавая различные функции, мы можем решить ряд задач, таких как сумма квадратов или кубов и факториал.

Затем давайте воспользуемся этой функцией, чтобы написать еще одну функциюsumSquares, которая суммирует квадраты целых чисел:

@Test
def whenCalledWithSumAndSquare_thenCorrectValue = {
  def square(x: Int) = x * x
  def sum(x: Int, y: Int) = x + y

  def sumSquares(a: Int, b: Int) =
    mapReduce(sum, 0, square, a, b)

  assertEquals(385, sumSquares(1, 10))
}

Выше видно, чтоhigher-order functions tend to create many small single-use functions. We can avoid naming them by using anonymous functions.

5.3. Анонимные функции

Анонимная функция — это выражение, которое оценивает функцию. Это похоже на лямбда-выражение в Java.

Давайте перепишем предыдущий пример, используя анонимные функции:

@Test
def whenCalledWithAnonymousFunctions_thenCorrectValue = {
  def sumSquares(a: Int, b: Int) =
    mapReduce((x, y) => x + y, 0, x => x * x, a, b)
  assertEquals(385, sumSquares(1, 10))
}

В этом примереmapReduce получает две анонимные функции:(x, y) ⇒ x + y иx ⇒ x * x.

Scala can deduce the parameter types from context. Поэтому мы опускаем тип параметров в этих функциях.

Это приводит к более краткому коду по сравнению с предыдущим примером.

5.4. Функции карри

A curried function takes multiple argument lists, such as def f(x: Int) (y: Int). Он применяется путем передачи нескольких списков аргументов, как в f (5) (6).

It is evaluated as an invocation of a chain of functions. Эти промежуточные функции принимают единственный аргумент и возвращают функцию.

Мы также можем частично указать списки аргументов, напримерf(5).

Теперь давайте разберемся с этим на примере:

@Test
def whenSumModCalledWith6And10_then10 = {
  // a curried function
  def sum(f : Int => Int)(a : Int, b : Int) : Int =
    if (a > b) 0 else f(a) + sum(f)(a + 1, b)

  // another curried function
  def mod(n : Int)(x : Int) = x % n

  // application of a curried function
  assertEquals(1, mod(5)(6))

  // partial application of curried function
  // trailing underscore is required to
  // make function type explicit
  val sumMod5 = sum(mod(5)) _

  assertEquals(10, sumMod5(6, 10))
}

Выше,sum иmod принимают по два списка аргументов. Мы передаем два списка аргументов, напримерmod(5)(6). Это оценивается как два вызова функции. Сначала оцениваетсяmod(5), который возвращает функцию. Это, в свою очередь, вызывается с аргументом6. . В результате мы получаем 1.

Можно частично применить параметры, так как вmod(5).  в результате мы получаем функцию.

Точно так же в выраженииsum(mod(5)) _ мы передаем только первый аргумент функцииsum. Следовательно,sumMod5 — функция.

Подчеркивание используется в качестве заполнителя для неприменяемых аргументов. Поскольку компилятор не может сделать вывод, что тип функции ожидается, мы используем завершающее подчеркивание, чтобы сделать возвращаемый тип функции явным.

5.5. Параметры имени

Функция может применять параметры двумя различными способами — по значению и по имени — она ​​оценивает аргументы по значению только один раз во время вызова. В отличие от этого, он оценивает аргументы по имени каждый раз, когда они ссылаются. Если аргумент по имени не используется, он не оценивается.

Scala по умолчанию использует параметры по значению. Если перед типом параметра стоит стрелка (⇒), он переключается на параметр по имени.

Теперь давайте воспользуемся им для реализации цикла while:

def whileLoop(condition: => Boolean)(body: => Unit): Unit =
  if (condition) {
    body
    whileLoop(condition)(body)
  }

Чтобы указанная выше функция работала правильно, оба параметраcondition иbody должны оцениваться каждый раз при обращении к ним. Поэтому мы определяем их как параметры по имени.

6. Определение класса

Мы определяем класс с ключевым словомclass, за которым следует имя класса.

После имениwe can specify primary constructor parameters. Doing so automatically adds members with the same name to the class.

В теле класса мы определяем члены — значения, переменные, методы и т. Д. They are public by default unless modified by the private or protected access modifiers.с

Мы должны использовать ключевое словоoverride, чтобы переопределить метод из суперкласса.

Давайте определим класс Employee:

class Employee(val name : String, var salary : Int, annualIncrement : Int = 20) {
  def incrementSalary() : Unit = {
    salary += annualIncrement
  }

  override def toString =
    s"Employee(name=$name, salary=$salary)"
}

Здесь мы указываем три параметра конструктора —name,salary иannualIncrement.

Поскольку мы объявляемname иsalary с ключевыми словамиval иvar, соответствующие члены являются общедоступными. С другой стороны, мы не используем ключевое словоval илиvar для параметраannualIncrement. Поэтому соответствующий член является частным. Поскольку мы указываем значение по умолчанию для этого параметра, мы можем опустить его при вызове конструктора.

Помимо полей, мы определяем методincrementSalary. Этот метод является публичным.

Затем давайте напишем модульный тест для этого класса:

@Test
def whenSalaryIncremented_thenCorrectSalary = {
  val employee = new Employee("John Doe", 1000)
  employee.incrementSalary()
  assertEquals(1020, employee.salary)
}

6.1. Абстрактный класс

Мы используем ключевое словоabstract, чтобы сделать класс абстрактным. Это похоже на Java. Он может иметь всех членов, которые могут быть в обычном классе.

Кроме того, он может содержать абстрактные члены. Это члены с простым объявлением и без определения, их определение приведено в подклассе.

Подобно Java, мы не можем создать экземпляр абстрактного класса.

Теперь давайте проиллюстрируем абстрактный класс на примере.

Во-первых, давайте создадим абстрактный классIntSet для представления набора целых чисел:

abstract class IntSet {
  // add an element to the set
  def incl(x: Int): IntSet

  // whether an element belongs to the set
  def contains(x: Int): Boolean
}

Затем давайте создадим конкретный подклассEmptyIntSet для представления пустого набора:

class EmptyIntSet extends IntSet {
  def contains(x : Int) = false
  def incl(x : Int) =
  new NonEmptyIntSet(x, this)
}

Затем другой подклассNonEmptyIntSet представляет непустые множества:

class NonEmptyIntSet(val head : Int, val tail : IntSet)
  extends IntSet {

  def contains(x : Int) =
    head == x || (tail contains x)

  def incl(x : Int) =
    if (this contains x) {
      this
    } else {
      new NonEmptyIntSet(x, this)
    }
}

Наконец, давайте напишем модульный тест дляNonEmptySet:

@Test
def givenSetOf1To10_whenContains11Called_thenFalse = {
  // Set up a set containing integers 1 to 10.
  val set1To10 = Range(1, 10)
    .foldLeft(new EmptyIntSet() : IntSet) {
        (x, y) => x incl y
    }

  assertFalse(set1To10 contains 11)
}

6.2. Черты

Черты соответствуют интерфейсам Java со следующими отличиями:

  • может расширяться из класса

  • может получить доступ к членам суперкласса

  • может иметь операторы инициализатора

Мы определяем их так же, как и классы, но используя ключевое словоtrait. Кроме того, они могут иметь те же члены, что и абстрактные классы, за исключением параметров конструктора. Кроме того, они предназначены для добавления в какой-то другой класс как миксин.

Теперь давайте проиллюстрируем черты характера на примере.

Во-первых, давайте определим чертуUpperCasePrinter, чтобы методtoString возвращал значение в верхнем регистре:

trait UpperCasePrinter {
  override def toString =
    super.toString toUpperCase
}

Затем давайте проверим эту черту, добавив ее в классEmployee:

@Test
def givenEmployeeWithTrait_whenToStringCalled_thenUpper = {
  val employee = new Employee("John Doe", 10) with UpperCasePrinter
  assertEquals("EMPLOYEE(NAME=JOHN DOE, SALARY=10)", employee.toString)
}

Классы, объекты и признаки могут наследовать не более одного класса, но любое количество признаков.

7. Определение объекта

Объекты являются экземплярами класса. Как мы видели в предыдущих примерах, мы создаем объекты из класса, используя ключевое словоnew.

Однако, если у класса может быть только один экземпляр, нам нужно предотвратить создание нескольких экземпляров. В Java мы используем шаблон Singleton для достижения этой цели.

Для таких случаев у нас есть краткий синтаксис, называемый определением объекта — похожий на определение класса с одним отличием. Instead of using the class keyword, we use the object keyword. Это определяет класс и лениво создает его единственный экземпляр.

Мы используем определения объектов для реализации служебных методов и синглтонов.

Давайте определим объектUtils:

object Utils {
  def average(x: Double, y: Double) =
    (x + y) / 2
}

Здесь мы определяем классUtils, а также создаем его единственный экземпляр.

We refer to this sole instance using its nameUtils. Этот экземпляр создается при первом обращении к нему.

Мы не можем создать еще один экземпляр Utils, используя ключевое словоnew.

Теперь давайте напишем модульный тест для объектаUtils:

assertEquals(15.0, Utils.average(10, 20), 1e-5)

7.1. Объект-компаньон и класс-компаньон

Если класс и определение объекта имеют одинаковые имена, мы называем их как сопутствующий класс и сопутствующий объект соответственно. Нам нужно определить оба в одном файле. Сопутствующие объекты могут получать доступ к закрытым членам из своего класса-компаньона и наоборот.

В отличие от Java,we do not have static members. Вместо этого мы используем сопутствующие объекты для реализации статических членов.

8. Сопоставление с образцом

Pattern matching matches an expression to a sequence of alternatives.  Каждый из них начинается с ключевого словаcase. За ним следует узор, разделительная стрелка (⇒) и ряд выражений. Выражение оценивается, если шаблон соответствует.

Мы можем построить шаблоны из:

  • конструкторы класса case

  • переменная структура

  • шаблон подстановки _

  • литералы

  • постоянные идентификаторы

Классы Case упрощают сопоставление с образцом для объектов. Мы добавляем ключевое словоcase при определении класса, чтобы сделать его классом case.

Таким образом, сопоставление с образцом намного мощнее, чем оператор switch в Java. По этой причине это широко используемая языковая функция.

Теперь давайте напишем метод Фибоначчи с использованием сопоставления с образцом:

def fibonacci(n:Int) : Int = n match {
  case 0 | 1 => 1
  case x if x > 1 =>
    fibonacci (x-1) + fibonacci(x-2)
}

Теперь давайте напишем модульный тест для этого метода:

assertEquals(13, fibonacci(6))

9. Заключение

В этом уроке мы представили язык Scala и некоторые его основные функции. Как мы уже видели, он обеспечивает отличную поддержку императивного, функционального и объектно-ориентированного программирования.

Как обычно, полный исходный код можно найтиover on GitHub.

Понравилась статья? Поделить с друзьями:
  • Мануал kymco super 9
  • Мой личный банк копилка инструкция как сменить пароль
  • Автомат живая вода инструкция по применению
  • Риофлора иммуно инструкция по применению взрослым в капсулах от чего
  • Лексофлон инструкция по применению для животных