web/ru/basics.textile (247 lines of code) (raw):

--- layout: post title: Основы next: basics2.textile --- В этом уроке вы узнаете: * "Обзор лекций":#overview * "Выражения":#expressions * "Переменные":#val * "Функции":#functions * "Классы":#class * "Основы наследования":#extends * "Трейты":#trait * "Типы":#types h2(#overview). Обзор лекций В первые несколько недель вы узнаете об основах синтаксиса и основных идеях, заложенных в язык Scala. Позже мы начнем раскрывать подробности на примерах. Некоторые примеры будем писать прямо в интерпретаторе, другие в исходном файле. Имея под рукой интерпретатор, можно легко исследовать проблемы. h3. Почему Scala? * Выразительность ** Функции первого класса ** Замыкания * Краткость ** Вывод типов ** Буквенный синтаксис для создания функции * Взаимодействие с Java ** Можно использовать Java библиотеки ** Можно использовать Java инструменты ** Нет потерь производительности h3. Как работает Scala? * Компиляция в байт-код Java * Работает с любой стандартной JVM ** Или даже с некоторыми нестандартными JVM, например Dalvik ** Компилятор Scala написан автором компилятора Java h3. Что еще нужно знать о Scala Scala лучше Java в некоторых аспектах. Перед тем как начинать изучать язык Scala, очистите свой разум, из этого выйдет больше толку. h3. Запускаем интерпретатор Наберите в консоли <code>sbt console</code>. <pre> $ sbt console [...] Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20). Type in expressions to have them evaluated. Type :help for more information. scala> </pre> h2(#expressions). Выражения <pre> scala> 1 + 1 res0: Int = 2 </pre> res0 - автоматически создаваемое имя для переменной, которое интерпретатор дает результату вашего выражения. Переменная имеет тип Int и содержит целочисленное значение 2. Все (ну, почти) в Scala - выражение. h2(#val). Константы Вы можете присвоить собственное имя результату выражения. <pre> scala> val two = 1 + 1 two: Int = 2 </pre> Для переменной с ключевым словом val вы не можете изменить ранее присвоенное значение. h3. Переменные Если вам нужно изменить значение константы, вы должны использовать ключевое слово <code>var</code> <pre> scala> var name = "steve" name: java.lang.String = steve scala> name = "marius" name: java.lang.String = marius </pre> h2(#functions). Функции Вы можете создать функцию с помощью ключевого слова def. <pre> scala> def addOne(m: Int): Int = m + 1 addOne: (m: Int)Int </pre> В Scala, вам нужно точно указывать тип, который принимает переменная в параметрах функции. К счастью, интерпретатор возвращает используемый функцией тип обратно. <pre> scala> val three = addOne(2) three: Int = 3 </pre> Вы можете опускать скобки при использовании функций, если они не имеют аргументов. <pre> scala> def three() = 1 + 2 three: ()Int scala> three() res2: Int = 3 scala> three res3: Int = 3 </pre> h3. Анонимные функции Вы можете создавать анонимные функции. <pre> scala> (x: Int) => x + 1 res2: (Int) => Int = <function1> </pre> Эта функция увеличит на 1 значение, которое было передано в анонимную функцию; значение именуется как x. <pre> scala> res2(1) res3: Int = 2 </pre> Вы можете передавать анонимные функции как параметры или сохранять их в переменных. <pre> scala> val addOne = (x: Int) => x + 1 addOne: (Int) => Int = <function1> scala> addOne(1) res4: Int = 2 </pre> Если ваша функция состоит из множества выражений, вы можете использовать фигурные скобки {}, чтобы обезопасить себя. <pre> def timesTwo(i: Int): Int = { println("hello world") i * 2 } </pre> Тоже самое верно и для анонимной функции <pre> scala> { i: Int => println("hello world") i * 2 } res0: (Int) => Int = <function1> </pre> Вы часто будете видеть подобный синтаксис при передачи анонимной функции в качестве параметра. h3. Частичный вызов функций Вы можете использовать частичный вызов функций, обозначаемый знаком нижнего подчеркивания(_), этот знак позже будет подменен вызовом функции. <pre> scala> def adder(m: Int, n: Int) = m + n adder: (m: Int,n: Int)Int </pre> <pre> scala> val add2 = adder(2, _:Int) add2: (Int) => Int = <function1> scala> add2(3) res50: Int = 5 </pre> Вы можете использовать частичный вызов функций с любым аргументом из списка аргументов, а не только с последним из них, как в примере. h3. Каррирование функций Иногда требуется передача каких-то аргументов в вашу функцию прямо сейчас, а других через некоторое время. Ниже пример функции, которая позволяет умножать два числа. В одном месте вызова функции вы решите, какой из аргументов будет множителем, а позднее вызывая функцию, вы сможете установить множимое. <pre> scala> def multiply(m: Int)(n: Int): Int = m * n multiply: (m: Int)(n: Int)Int </pre> Вы можете вызвать функцию напрямую с двумя аргументами. <pre> scala> multiply(2)(3) res0: Int = 6 </pre> Вы можете передать первый аргумент, а второй аргумент объявить как частично вызываемый. <pre> scala> val timesTwo = multiply(2) _ timesTwo: (Int) => Int = <function1> scala> timesTwo(3) res1: Int = 6 </pre> Вы можете взять любую функцию с множеством аргументов и произвести ее каррирование. Давайте попробуем использовать функцию, которую рассматривали раньше, например <code>adder</code> <pre> scala> (adder _).curried res1: (Int) => (Int) => Int = <function1> </pre> См. подробнее о <a href="https://ru.wikipedia.org/wiki/%D0%9A%D0%B0%D1%80%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5">Каррировании</a> h3. Использование переменного количества аргументов Существует специальный синтаксис для методов, которые могут принимать параметры одного и того же типа. <pre> def capitalizeAll(args: String*) = { args.map { arg => arg.capitalize } } scala> capitalizeAll("rarity", "applejack") res2: Seq[String] = ArrayBuffer(Rarity, Applejack) </pre> h2(#class). Классы <pre> scala> class Calculator { | val brand: String = "HP" | def add(m: Int, n: Int): Int = m + n | } // Здесь мы объявили класс Calculator scala> val calc = new Calculator calc: Calculator = Calculator@e75a11 scala> calc.add(1, 2) res1: Int = 3 scala> calc.brand res2: String = "HP" </pre> В примере объявляется метод и поле с ключевым словом val. Методы - это функции, которые имеют доступ к внутренним сущностям класса. h3. Конструктор Конструкторы не являются специальными методами, их код находится в классе за пределами определения метода. Давайте расширим наш пример с калькулятором. Будем принимать аргумент конструктора и использовать его для инициализации внутреннего состояния. <pre> class Calculator(brand: String) { /** * Конструктор. */ val color: String = if (brand == "TI") { "blue" } else if (brand == "HP") { "black" } else { "white" } // Метод экземпляра класса. def add(m: Int, n: Int): Int = m + n } </pre> Обратите внимание на два различных способа написания комментариев. h3. Выражения Наш пример с калькулятором дает хороший пример того, как Scala ориентирован на выражения (expression-oriented). Значение переменной color было присвоено благодаря if/else выражению. Scala сильно ориентирован на выражения: большинство вещей делается с помощью выражений, а не утверждений. h2(#extends). Наследование <pre> class ScientificCalculator(brand: String) extends Calculator(brand) { def log(m: Double, base: Double) = math.log(m) / math.log(base) } </pre> *Смотрите также:* В Effective Scala указывается на то, что лучше использовать <a href="https://twitter.github.com/effectivescala/index-ru.html#Типы и обобщенные типы-Псевдонимы типов">Псевдонимы типов</a> вместо <code>extends</code>, особенно если подкласс практически ничем не отличается от суперкласса. В "Туре по языку Scala" вы найдете более подробное описание <a href="https://www.scala-lang.org/node/125">Подклассов</a>. h3. Перегрузка методов <pre> class EvenMoreScientificCalculator(brand: String) extends ScientificCalculator(brand) { def log(m: Int): Double = log(m, math.exp(1)) } </pre> h2(#trait). Трейты <code>Трейты</code> - это коллекция полей и методов, которые вы можете расширить или примешать к вашему классу. <pre> trait Car { val brand: String } </pre> <pre> class BMW extends Car { val brand = "BMW" } </pre> *Смотрите также:* В Effective Scala есть описание <a href="https://twitter.github.com/effectivescala/index-ru.html#Объектно-ориентированное программирование-Трейты(Traits)">Трейтов</a>. h2(#types). Типы Ранее вы могли видеть, что мы определили функцию, принимающая тип <code>Int</code>, который является одним из видов Number. Функция может быть объявлена как обобщенная (generic) и после этого может работать с любым типом. Когда объявлена такая функция, вы увидите <pre>параметр-тип</pre> размещенный внутри квадратных скобок: Вы можете думать о них, как о множестве параметров-типов. Рассмотрим пример трейта Кэш (Cache), который принимает параметры-типы (K, V) для ключей и их значений. <pre> trait Cache[K, V] { def get(key: K): V def put(key: K, value: V) def delete(key: K) } </pre> Методы тоже могут иметь параметры-типы <pre> def remove[K](key: K) </pre>