From 1df41364793ae538a2ba42695f8dd4fc24db4c7b Mon Sep 17 00:00:00 2001 From: "Kotobotov.ru" Date: Fri, 18 Jan 2019 16:04:48 +0700 Subject: [PATCH 1/5] :memo: ru_scala_tour full 36 docs Took 4 minutes --- _data/translations.yml | 2 +- _ru/tour/abstract-type-members.md | 77 ++++++ _ru/tour/annotations.md | 129 ++++++++++ _ru/tour/automatic-closures.md | 63 +++++ _ru/tour/basics.md | 322 ++++++++++++++++++++++++ _ru/tour/by-name-parameters.md | 42 ++++ _ru/tour/case-classes.md | 59 +++++ _ru/tour/classes.md | 110 ++++++++ _ru/tour/compound-types.md | 54 ++++ _ru/tour/default-parameter-values.md | 49 ++++ _ru/tour/extractor-objects.md | 65 +++++ _ru/tour/for-comprehensions.md | 66 +++++ _ru/tour/generic-classes.md | 59 +++++ _ru/tour/higher-order-functions.md | 109 ++++++++ _ru/tour/implicit-conversions.md | 63 +++++ _ru/tour/implicit-parameters.md | 73 ++++++ _ru/tour/inner-classes.md | 81 ++++++ _ru/tour/lower-type-bounds.md | 70 ++++++ _ru/tour/mixin-class-composition.md | 83 ++++++ _ru/tour/multiple-parameter-lists.md | 80 ++++++ _ru/tour/named-arguments.md | 34 +++ _ru/tour/nested-functions.md | 38 +++ _ru/tour/operators.md | 81 ++++++ _ru/tour/package-objects.md | 72 ++++++ _ru/tour/packages-and-imports.md | 86 +++++++ _ru/tour/pattern-matching.md | 151 +++++++++++ _ru/tour/polymorphic-methods.md | 36 +++ _ru/tour/regular-expression-patterns.md | 61 +++++ _ru/tour/self-types.md | 40 +++ _ru/tour/singleton-objects.md | 107 ++++++++ _ru/tour/tour-of-scala.md | 64 +++++ _ru/tour/traits.md | 84 +++++++ _ru/tour/tuples.md | 95 +++++++ _ru/tour/type-inference.md | 75 ++++++ _ru/tour/unified-types.md | 82 ++++++ _ru/tour/upper-type-bounds.md | 54 ++++ _ru/tour/variances.md | 154 ++++++++++++ 37 files changed, 2969 insertions(+), 1 deletion(-) create mode 100644 _ru/tour/abstract-type-members.md create mode 100644 _ru/tour/annotations.md create mode 100644 _ru/tour/automatic-closures.md create mode 100644 _ru/tour/basics.md create mode 100644 _ru/tour/by-name-parameters.md create mode 100644 _ru/tour/case-classes.md create mode 100644 _ru/tour/classes.md create mode 100644 _ru/tour/compound-types.md create mode 100644 _ru/tour/default-parameter-values.md create mode 100644 _ru/tour/extractor-objects.md create mode 100644 _ru/tour/for-comprehensions.md create mode 100644 _ru/tour/generic-classes.md create mode 100644 _ru/tour/higher-order-functions.md create mode 100644 _ru/tour/implicit-conversions.md create mode 100644 _ru/tour/implicit-parameters.md create mode 100644 _ru/tour/inner-classes.md create mode 100644 _ru/tour/lower-type-bounds.md create mode 100644 _ru/tour/mixin-class-composition.md create mode 100644 _ru/tour/multiple-parameter-lists.md create mode 100644 _ru/tour/named-arguments.md create mode 100644 _ru/tour/nested-functions.md create mode 100644 _ru/tour/operators.md create mode 100644 _ru/tour/package-objects.md create mode 100644 _ru/tour/packages-and-imports.md create mode 100644 _ru/tour/pattern-matching.md create mode 100644 _ru/tour/polymorphic-methods.md create mode 100644 _ru/tour/regular-expression-patterns.md create mode 100644 _ru/tour/self-types.md create mode 100644 _ru/tour/singleton-objects.md create mode 100644 _ru/tour/tour-of-scala.md create mode 100644 _ru/tour/traits.md create mode 100644 _ru/tour/tuples.md create mode 100644 _ru/tour/type-inference.md create mode 100644 _ru/tour/unified-types.md create mode 100644 _ru/tour/upper-type-bounds.md create mode 100644 _ru/tour/variances.md diff --git a/_data/translations.yml b/_data/translations.yml index 3641a04203..266b039905 100644 --- a/_data/translations.yml +++ b/_data/translations.yml @@ -1,2 +1,2 @@ tour: - languages: [ba, es, ko, pt-br, pl, zh-cn, th] + languages: [ba, es, ko, pt-br, pl, zh-cn, th, ru] diff --git a/_ru/tour/abstract-type-members.md b/_ru/tour/abstract-type-members.md new file mode 100644 index 0000000000..be69a91a93 --- /dev/null +++ b/_ru/tour/abstract-type-members.md @@ -0,0 +1,77 @@ +--- +layout: tour +title: Члены Абстрактного Типа + +discourse: true + +partof: scala-tour +num: 23 +language: ru +next-page: compound-types +previous-page: inner-classes +topics: abstract type members +prerequisite-knowledge: variance, upper-type-bound + +--- + +Абстрактные типы, такие как трейты и абстрактные классы, которые могут содержать членов абстрактного типа. +Это означает, что только непосредственно реализация определяет, каким именно будет этот тип. +Вот пример: + +```tut +trait Buffer { + type T + val element: T +} +``` +Здесь мы определили абстрактный вариант тип `T`, который используется для описания типа `element`. Мы можем расширить его в абстрактном классе, добавив верхнюю границу связанного с `T` типа, чтобы сделать его более определенным. + +```tut +abstract class SeqBuffer extends Buffer { + type U + type T <: Seq[U] + def length = element.length +} +``` +Обратите внимание, как мы можем использовать еще один абстрактный абстрактный тип `type U` в качестве верхней границы типа. Класс `SeqBuffer` позволяет хранить в буфере только последовательности, указывая, что тип `T` должен быть подтипом `Seq[U]` для нового абстрактного типа `U`. + +[Трейты](traits.html) или [классы](classes.html) с абстрактными членами типа часто используются в сочетании с анонимными экземплярами классов. Чтобы проиллюстрировать это рассмотрим программу, которая имеет дело с буфером, который ссылается на список целых чисел: + +```tut +abstract class IntSeqBuffer extends SeqBuffer { + type U = Int +} + + +def newIntSeqBuf(elem1: Int, elem2: Int): IntSeqBuffer = + new IntSeqBuffer { + type T = List[U] + val element = List(elem1, elem2) + } +val buf = newIntSeqBuf(7, 8) +println("length = " + buf.length) +println("content = " + buf.element) +``` +Здесь класс `newIntSeqBuf` является создателем экземпляров `IntSeqBuffer`, использует анонимную реализацию класса `IntSeqBuffer` (т.е. `new IntSeqBuffer`), устанавливая тип `T` как `List[Int]`. + +Мы можем выводить тип класса из типа его членов и наоборот наоборот. Приведем версию кода, в которой выводится тип класса из типа его члена: + +```tut +abstract class Buffer[+T] { + val element: T +} +abstract class SeqBuffer[U, +T <: Seq[U]] extends Buffer[T] { + def length = element.length +} + +def newIntSeqBuf(e1: Int, e2: Int): SeqBuffer[Int, Seq[Int]] = + new SeqBuffer[Int, List[Int]] { + val element = List(e1, e2) + } + +val buf = newIntSeqBuf(7, 8) +println("length = " + buf.length) +println("content = " + buf.element) +``` + +Обратите внимание, что здесь нам необходимо использовать [вариантность в описании типа](variances.html) (`+T <: Seq[U]`) для того, чтобы скрыть конкретный тип реализации последовательности объектов, возвращаемых из метода `newIntSeqBuf`. \ No newline at end of file diff --git a/_ru/tour/annotations.md b/_ru/tour/annotations.md new file mode 100644 index 0000000000..c97c226854 --- /dev/null +++ b/_ru/tour/annotations.md @@ -0,0 +1,129 @@ +--- +layout: tour +title: Аннотации + +discourse: true + +partof: scala-tour + +num: 32 +language: ru +next-page: default-parameter-values +previous-page: by-name-parameters + +--- + +Аннотации используются для передачи метаданных при объявлении. Например, аннотация `@deprecated` перед объявлением метода, заставит компилятор вывести предупреждение, если этот метод будет использован. +``` +object DeprecationDemo extends App { + @deprecated("deprecation message", "release # which deprecates method") + def hello = "hola" + + hello +} +``` +Такой код скомпилируется, но компилятор выдаст предупреждение: "there was one deprecation warning". + +Аннотация применяется к первому идущему после нее объявлению или определению. Допускается использование сразу нескольких аннотаций следующих друг за другом. Порядок, в котором приводятся аннотации, не имеет значения. + +## Аннотации, обеспечивающие корректность работы кода +Некоторые аннотации приводят к невозможности компиляции, если условие (условия) не выполняется. Например, аннотация `@tailrec` гарантирует, что метод является [хвостовой рекурсией](https://ru.wikipedia.org/wiki/%D0%A5%D0%B2%D0%BE%D1%81%D1%82%D0%BE%D0%B2%D0%B0%D1%8F_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F). Хвостовая рекурсия помогает держать потребление памяти на постоянном уровне. Вот как она используется в методе, который вычисляет факториал: + +```tut +import scala.annotation.tailrec + +def factorial(x: Int): Int = { + + @tailrec + def factorialHelper(x: Int, accumulator: Int): Int = { + if (x == 1) accumulator else factorialHelper(x - 1, accumulator * x) + } + factorialHelper(x, 1) +} +``` +Метод `factorialHelper` имеет аннотацию `@tailrec`, которая гарантирует, что метод действительно является хвостовой рекурсией. Если бы мы изменили реализацию `factorialHelper` так как указано далее, то компиляция бы провалилась: +``` +import scala.annotation.tailrec + +def factorial(x: Int): Int = { + @tailrec + def factorialHelper(x: Int): Int = { + if (x == 1) 1 else x * factorialHelper(x - 1) + } + factorialHelper(x) +} +``` +Мы бы получили сообщение "Recursive call not in tail position"(Рекурсивный вызов не в хвостовой позиции). + + +## Аннотации, влияющие на генерацию кода +Некоторые аннотации типа `@inline` влияют на сгенерированный код (т.е. ваш jar-файл может иметь другой код, чем если бы вы не использовали аннотацию). Такая аннотация означает вставку всего кода в тело метода вместо вызова. Полученный байт-код длиннее, но, надеюсь, работает быстрее. Использование аннотации `@inline` не гарантирует, что метод будет встроен, но заставит компилятор сделать это, если и только если будут соблюдены некоторые практические требования к размеру сгенерированного кода. + +### Java аннотации ### +Есть некоторые отличий синтаксиса аннотаций, если пишется Scala код, который взаимодействует с Java. + +**Примечание:**Убедитесь, что вы используете опцию `-target:jvm-1.8` с аннотациями Java. + +Java имеет определяемые пользователем метаданные в виде [аннотаций](https://docs.oracle.com/javase/tutorial/java/annotations/). Ключевой особенностью аннотаций является то, что они полагаются на указание пар ключ-значение для инициализации своих элементов. Например, если нам нужна аннотация для отслеживания источника какого-то класса, мы можем определить её как + +``` +@interface Source { + public String URL(); + public String mail(); +} +``` + +А затем использовать следующим образом + +``` +@Source(URL = "http://coders.com/", + mail = "support@coders.com") +public class MyClass extends HisClass ... +``` + +Использование аннотации в Scala похоже на вызов конструктора. Для создания экземпляра из Java аннотации необходимо использовать именованные аргументы: + +``` +@Source(URL = "http://coders.com/", + mail = "support@coders.com") +class MyScalaClass ... +``` + +Этот синтаксис достаточно перегруженный, если аннотация содержит только один элемент (без значения по умолчанию), поэтому, если имя указано как `value`, оно может быть применено в Java с помощью конструктора-подобного синтаксиса: + +``` +@interface SourceURL { + public String value(); + public String mail() default ""; +} +``` + +А затем использовать следующим образом + +``` +@SourceURL("http://coders.com/") +public class MyClass extends HisClass ... +``` + +В такомже случае Scala предоставляет такую же возможность + +``` +@SourceURL("http://coders.com/") +class MyScalaClass ... +``` + +Элемент `mail` был указан со значением по умолчанию, поэтому нам не нужно явно указывать его значение. Однако, если нам нужно это сделать, мы не можем смешивать эти два стиля в Java: + +``` +@SourceURL(value = "http://coders.com/", + mail = "support@coders.com") +public class MyClass extends HisClass ... +``` + +Scala обеспечивает большую гибкость в этом отношении + +``` +@SourceURL("http://coders.com/", + mail = "support@coders.com") + class MyScalaClass ... +``` diff --git a/_ru/tour/automatic-closures.md b/_ru/tour/automatic-closures.md new file mode 100644 index 0000000000..057788c251 --- /dev/null +++ b/_ru/tour/automatic-closures.md @@ -0,0 +1,63 @@ +--- +layout: tour +title: Конструкция Автоматического Замыкания Зависимого Типа + +discourse: true +language: ru +partof: scala-tour +num: 14 +--- + +Scala допускает использование в качестве параметров методов имена безпараметрических функций. При вызове такого метода фактические параметры для безпараметрических функций не вычисляются, а передается функция с нулем аргументов, которая захватывает вычисление соответствующего параметра (так называемый *вызов по имени*). + +Следующий код демонстрирует этот механизм: + + object TargetTest1 extends Application { + def whileLoop(cond: => Boolean)(body: => Unit): Unit = + if (cond) { + body + whileLoop(cond)(body) + } + var i = 10 + whileLoop (i > 0) { + println(i) + i -= 1 + } + } + +Функция whileLoop принимает два параметра `cond` и `body`. При использовании функции значения этих параметров не вычисляются. Но всякий раз, когда параметры используются в теле `whileLoop`, их значение будет вычислятся заново через использование автоматически созданных неявно вызываемых функций. Таким образом, наш метод `whileLoop` реализует Java-подобный цикл while-loop со схемой рекурсивной реализации. + +Мы можем комбинировать использование [инфиксных/постфиксных операторов](operators.html) с этим механизмом для создания более сложных выражений (с хорошим синтаксисом). + +Вот реализация loop-unless выражения: + + object TargetTest2 extends Application { + def loop(body: => Unit): LoopUnlessCond = + new LoopUnlessCond(body) + protected class LoopUnlessCond(body: => Unit) { + def unless(cond: => Boolean) { + body + if (!cond) unless(cond) + } + } + var i = 10 + loop { + println("i = " + i) + i -= 1 + } unless (i == 0) + } +Функция `loop` принимает только тело цикла и возвращает экземпляр класса `LoopUnlessCond` (который захватывает это тело цикла). Обратите внимание, что тело еще не вычислено. Класс `LoopUnlessCond` имеет метод `unless`, который мы можем использовать как *инфиксный оператор*. Таким образом, мы получаем вполне естественный синтаксис для нашего нового цикла: `loop { < выражение > } unless ( < условие > )`. + + +Ниже приведен вывод выполнения `TargetTest2`: + + i = 10 + i = 9 + i = 8 + i = 7 + i = 6 + i = 5 + i = 4 + i = 3 + i = 2 + i = 1 diff --git a/_ru/tour/basics.md b/_ru/tour/basics.md new file mode 100644 index 0000000000..70bbc6b0cb --- /dev/null +++ b/_ru/tour/basics.md @@ -0,0 +1,322 @@ +--- +layout: tour +title: Основы + +discourse: true + +partof: scala-tour + +num: 2 +language: ru +next-page: unified-types +previous-page: tour-of-scala + +--- + +На этой странице мы расскажем об основах Scala. + +## Попробовать Scala в браузере. + +Вы можете запустить Scala в браузере с помощью ScalaFiddle. + +1. Зайдите на [https://scalafiddle.io](https://scalafiddle.io). +2. Вставьте `println("Hello, world!")` в левую панель. +3. Нажмите кнопку "Run". Вывод отобразится в правой панели. + +Это простой способ поэкспериментировать со Scala кодом без всяких настроек. + +Большинство примеров кода в этой документации также интегрированы с ScalaFiddle, +поэтому вы можете непосредственно поэкспериментировать с ними, просто нажав кнопку Run. + +## Выражения + +Выражения - это вычислимые утверждения. +``` +1 + 1 +``` +Вы можете выводить результаты выражений используя `println`. + +{% scalafiddle %} +```tut +println(1) // 1 +println(1 + 1) // 2 +println("Hello!") // Hello! +println("Hello," + " world!") // Hello, world! +``` +{% endscalafiddle %} + +### Значения + +Результаты выражений можно назвать с помощью ключевого слова `val`. + +```tut +val x = 1 + 1 +println(x) // 2 +``` + +Названные результаты, такие как `x` в примере, называются значениями. +Вызов значения не приводит к его повторному вычислению. + +Значения константны и не могут быть переназначены. + +```tut:fail +x = 3 // Не компилируется. +``` + +Типы значений могут быть выведены автоматически, но вы также можете явно указать тип, как показано ниже: + +```tut +val x: Int = 1 + 1 +``` + +Обратите внимание, что объявление типа `Int` происходит после идентификатора `x`, следующим за `:`. + +### Переменные + +Переменные похожи на значения константы, за исключением того, что их можно переназначить заново. Вы можете объявить переменную с помощью ключевого слова `var`. + +```tut +var x = 1 + 1 +x = 3 // Компилируется потому что "x" объявлен с ключевым словом "var". +println(x * x) // 9 +``` + +Как и в случае со значениями, вы можете явно указать тип, если хотите: + +```tut +var x: Int = 1 + 1 +``` + + +## Блоки + +Вы можете комбинировать выражения, окружая их `{}`. Мы называем это блоком. + +Результат последнего выражения в блоке - будет результатом всего блока в целом. + +```tut +println({ + val x = 1 + 1 + x + 1 +}) // 3 +``` + +## Функции + +Функции - это выражения, которые принимают параметры. + +Вы можете определить анонимную функцию (т.е. без имени), которая возвращает переданное число прибавив к нему еденицу: + +```tut +(x: Int) => x + 1 +``` + +Слева от `=>` находится список параметров. Справа - выражение, связанное с параметрами. + +Вы также можете назвать функции. + +{% scalafiddle %} +```tut +val addOne = (x: Int) => x + 1 +println(addOne(1)) // 2 +``` +{% endscalafiddle %} + +Функции могут принимать множество параметров. + +{% scalafiddle %} +```tut +val add = (x: Int, y: Int) => x + y +println(add(1, 2)) // 3 +``` +{% endscalafiddle %} + +Или вообще не принимать никаких параметров. + +```tut +val getTheAnswer = () => 42 +println(getTheAnswer()) // 42 +``` + +## Методы + +Методы выглядят и ведут себя очень похоже на функции, но между ними есть несколько принципиальных различий. + +Методы задаются ключевым словом `def`. За `def` следует имя, список параметров, возвращаемый тип и тело. + +{% scalafiddle %} +```tut +def add(x: Int, y: Int): Int = x + y +println(add(1, 2)) // 3 +``` +{% endscalafiddle %} + +Обратите внимание, как объявлен возвращаемый тип сразу _после_ списка параметров и двоеточия `: Int`. + +Методы могут принимать несколько списков параметров. + +{% scalafiddle %} +```tut +def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier +println(addThenMultiply(1, 2)(3)) // 9 +``` +{% endscalafiddle %} + +Или вообще ни одного списка параметров. + +```tut +def name: String = System.getProperty("user.name") +println("Hello, " + name + "!") +``` + +Есть некоторые отличия от функций, но пока что их можно рассматривать как нечто похожее. + +Методы также могут иметь многострочные выражения. +```tut +def getSquareString(input: Double): String = { + val square = input * input + square.toString +} +``` +Последнее выражениее в теле становится возвращаемым значением метода. (У Scala есть ключевое слово `return` , но оно практически не используется.) + +## Классы + +Вы можете объявлять классы используя ключевое слово `class`, за которым следует его имя и параметры конструктора. + +```tut +class Greeter(prefix: String, suffix: String) { + def greet(name: String): Unit = + println(prefix + name + suffix) +} +``` +Возвращаемый тип метода `greet` это `Unit`, используется тогда, когда не имеет смысла что-либо возвращать. Аналогично `void` в Java и C. (Поскольку каждое выражение Scala должно иметь какое-то значение, поэтому, при оттуствии возвращающегося значения, возвращается одиночка (сингэлтон) типа Unit. Явным образом его можно задаеть как `()`, он не несет какой-либо информации.) + +Вы можете создать экземпляр класса используя ключевое слово `new`. + +```tut +val greeter = new Greeter("Hello, ", "!") +greeter.greet("Scala developer") // Hello, Scala developer! +``` + +Позже мы рассмотрим классы [подробнее](classes.html). + +## Классы образцы (Case Class) + +В Scala есть специальный тип класса, который называется классом образцом (case class). По умолчанию такие классы неизменны и сравниваются по значению из конструктора. Вы можете объявлять классы образцы с помощью ключевых слов `case class`. + +```tut +case class Point(x: Int, y: Int) +``` + +Можно создавать экземпляры класса образца без использования ключевого слова `new`. + +```tut +val point = Point(1, 2) +val anotherPoint = Point(1, 2) +val yetAnotherPoint = Point(2, 2) +``` + +Они сравниваются по значению. + +```tut +if (point == anotherPoint) { + println(point + " and " + anotherPoint + " are the same.") +} else { + println(point + " and " + anotherPoint + " are different.") +} // Point(1,2) и Point(1,2) одни и теже. + +if (point == yetAnotherPoint) { + println(point + " and " + yetAnotherPoint + " are the same.") +} else { + println(point + " and " + yetAnotherPoint + " are different.") +} // Point(1,2) и Point(2,2) разные. +``` + +Есть еще куча вещей которые мы бы хотели рассказать про классы образцы, мы уверены, что вы влюбитесь в них! Обязательно рассмотрим их подробнее немного [позже](case-classes.html). + +## Объекты + +Объекты задаются и существуют в единственным экземпляре. Вы можете думать о них как об одиночках своего собственного класса. + +Вы можете задать объекты при помощи ключевого слова `object`. + +```tut +object IdFactory { + private var counter = 0 + def create(): Int = { + counter += 1 + counter + } +} +``` + +Вы можете сразу получить доступ к объекту, ссылаясь на его имя. + +```tut +val newId: Int = IdFactory.create() +println(newId) // 1 +val newerId: Int = IdFactory.create() +println(newerId) // 2 +``` + +Позже рассмотрим объекты более [подробно](singleton-objects.html). + +## Трейты + +Трейты - как типы описывают харрактеристики классов, в нем могут объявляться определенные поля и методы. Трейты можно комбинировать. + +Объявить трейт можно с помощью ключевого слова `trait`. + +```tut +trait Greeter { + def greet(name: String): Unit +} +``` + +Трейты также могут иметь реализации методов и полей, которые предполагается использовать умолчанию. + +{% scalafiddle %} +```tut +trait Greeter { + def greet(name: String): Unit = + println("Hello, " + name + "!") +} +``` + +Вы можете наследовать свойства трейтов используя ключевое словом `extends` и переопределять реализацию с помощью ключевого слова `override`. + +```tut +class DefaultGreeter extends Greeter + +class CustomizableGreeter(prefix: String, postfix: String) extends Greeter { + override def greet(name: String): Unit = { + println(prefix + name + postfix) + } +} + +val greeter = new DefaultGreeter() +greeter.greet("Scala developer") // Hello, Scala developer! + +val customGreeter = new CustomizableGreeter("How are you, ", "?") +customGreeter.greet("Scala developer") // How are you, Scala developer? +``` +{% endscalafiddle %} + +Здесь `DefaultGreeter` наследуется только от одиного трейта, но можно наследоваться от нескольких. + +Позже мы рассмотрим трейты [подробнее](traits.html). + +## Главный метод + +Главный метод является отправной точкой в программе. +Для Виртуальной Java Машины требуется, чтобы главный метод назывался `main` и принимал один аргумент, массив строк. + +Используя объект, можно задать главный метод следующим образом: + +```tut +object Main { + def main(args: Array[String]): Unit = + println("Hello, Scala developer!") +} +``` diff --git a/_ru/tour/by-name-parameters.md b/_ru/tour/by-name-parameters.md new file mode 100644 index 0000000000..653f211585 --- /dev/null +++ b/_ru/tour/by-name-parameters.md @@ -0,0 +1,42 @@ +--- +layout: tour +title: Вызов по имени + +discourse: true + +partof: scala-tour + +num: 31 +language: ru +next-page: annotations +previous-page: operators + +--- + +_Вызов параметров по имени_ - это когда значение параметра вычисляется только в момент вызова параметра. Этот способ противоположен _вызову по значению_. Чтоб вызов параметра был по имени, необходимо просто указать `=>` перед его типом. +```tut +def calculate(input: => Int) = input * 37 +``` +Преимущество вызова параметров по имени заключается в том, что они не вычисляются если не используются в теле функции. С другой стороны плюсы вызова параметров по значению в том, что они вычисляются только один раз. + +Вот пример того, как мы можем реализовать условный цикл: + +```tut +def whileLoop(condition: => Boolean)(body: => Unit): Unit = + if (condition) { + body + whileLoop(condition)(body) + } + +var i = 2 + +whileLoop (i > 0) { + println(i) + i -= 1 +} // выведет 2 1 +``` +Метод `whileLoop` использует несколько списков параметров - условие и тело цикла. Если `condition` является верным, выполняется `body`, а затем выполняется рекурсивный вызов whileLoop. Если `condition` является ложным, то тело никогда не вычисляется, тк у нас стоит `=>` перед типом `body`. + +Теперь, когда мы передаем `i > 0` как наше условие `condition` и `println(i); i-= 1` как тело `body`, код ведет себя также как обычный цикл в большинстве языков программировния. + +Такая возможность откладывать вычисления параметра до его использования может помочь повысить производительность, отсекая не нужные вычисления при определенных условиях. diff --git a/_ru/tour/case-classes.md b/_ru/tour/case-classes.md new file mode 100644 index 0000000000..6d5b07a196 --- /dev/null +++ b/_ru/tour/case-classes.md @@ -0,0 +1,59 @@ +--- +layout: tour +title: Классы Образцы + +discourse: true + +partof: scala-tour + +num: 11 +language: ru +next-page: pattern-matching +previous-page: multiple-parameter-lists +prerequisite-knowledge: classes, basics, mutability + +--- + +Классы образцы похожи на обычные классы с несколькими ключевыми отличиями, о которых мы поговорим ниже. Классы образцы хороши для моделирования неизменяемых данных. На следующем странице обзора мы увидим, насколько они полезны для участия в [сопоставлении с примером](pattern-matching.html). + +## Объявление класса образца +Минимальный вариант объявления класса образца: указание ключевого слова `case class`, название и список параметров (которые могут быть пустыми). Пример: +```tut +case class Book(isbn: String) + +val frankenstein = Book("978-0486282114") +``` +Обратите внимание, что ключевое слово `new` не было использовано для создания экземпляра класса `Book`. Это связано с тем, что классы образцы по умолчанию имеют объект компаньон с методом `apply`, который берет на себя заботу о создании экземпляра класса. + +При создании класса образца с параметрами, эти параметры являются публичными и неизменяемыми. +``` +case class Message(sender: String, recipient: String, body: String) +val message1 = Message("guillaume@quebec.ca", "jorge@catalonia.es", "Ça va ?") + +println(message1.sender) // prints guillaume@quebec.ca +message1.sender = "travis@washington.us" // эта строка не компилируется +``` +Вы не можете переназначить `message1.sender`, потому что это `val` (т.е. константа). Возможно использовать `var`s в классах образцах, но это не рекомендуется. + +## Сравнение +Классы образцы сравниваются по структуре, а не по ссылкам: +``` +case class Message(sender: String, recipient: String, body: String) + +val message2 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?") +val message3 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?") +val messagesAreTheSame = message2 == message3 // true +``` +Даже если `message2` и `message3` ссылаются на разные объекты, значения каждого из них равна. + +## Копирование +Вы можете создать копию экземпляра класса образца, просто воспользовавшись методом `copy`. При этом по желанию можно изменить аргументы конструктора. +``` +case class Message(sender: String, recipient: String, body: String) +val message4 = Message("julien@bretagne.fr", "travis@washington.us", "Me zo o komz gant ma amezeg") +val message5 = message4.copy(sender = message4.recipient, recipient = "claire@bourgogne.fr") +message5.sender // travis@washington.us +message5.recipient // claire@bourgogne.fr +message5.body // "Me zo o komz gant ma amezeg" +``` +Получатель `message4` использует в качестве отправителя `message5`, кроме параметра `body` который был скопирован из `message4`. diff --git a/_ru/tour/classes.md b/_ru/tour/classes.md new file mode 100644 index 0000000000..82201cd3a6 --- /dev/null +++ b/_ru/tour/classes.md @@ -0,0 +1,110 @@ +--- +layout: tour +title: Классы + +discourse: true + +partof: scala-tour + +num: 4 +language: ru +next-page: traits +previous-page: unified-types +topics: classes +prerequisite-knowledge: no-return-keyword, type-declaration-syntax, string-interpolation, procedures + +--- + +Классы в Scala являются основами для создания объектов. Они могут содержать методы, константы, переменные, типы, объекты, трейты и классы, которые в совокупности называются _членами_. Типы, объекты и трейты будут рассмотрены позже в ходе нашего обзора. + +## Объявление класса +Минимальное объявление класса - это просто ключевое слово `class` и его имя. Имена классов должны быть написаны с заглавной буквы. +```tut +class User + +val user1 = new User +``` +Ключевое слово `new` используется для создания экземпляра класса. `User` имеет конструктор по умолчанию, который не принимает аргументов, так как конструктор не был определен. Однако обычно используется и конструктор, и тело класса. Пример объявления класса Point приведен ниже: + +```tut +class Point(var x: Int, var y: Int) { + + def move(dx: Int, dy: Int): Unit = { + x = x + dx + y = y + dy + } + + override def toString: String = + s"($x, $y)" +} + +val point1 = new Point(2, 3) +point1.x // 2 +println(point1) // prints (2, 3) +``` + +В этом классе у `Point` есть четыре члена: переменные `x` и `y` и методы `move` и `toString`. +В отличие от многих других языков, основной конструктор находится в сигнатуре класса `(var x: Int, var y: Int)`. Метод `move` принимает два целочисленных аргумента и возвращает значение Unit `()` - это пустое множество, которое не содержит никакой информации. Примерно соответствует `void` в Java-подобных языках. С другой стороны, `toString` не принимает никаких аргументов, а возвращает значение `String`. Поскольку `toString` переопределяет `toString` из [`AnyRef`](unified-types.html), он помечается ключевым словом `override`. + +## Конструкторы + +Конструкторы могут иметь необязательные параметры, если указать их значения по умолчанию как в примере: + +```tut +class Point(var x: Int = 0, var y: Int = 0) + +val origin = new Point // x и y оба равны 0 +val point1 = new Point(1) +println(point1.x) // выводит 1 + +``` + +В этой версии класса `Point`, `x` и `y` имеют значение по умолчанию `0`, поэтому аргументов не требуется. Однако, поскольку конструктор считывает аргументы слева направо, если вы просто хотите передать значение `y`, то вам нужно будет указать завадаемый параметр. +``` +class Point(var x: Int = 0, var y: Int = 0) +val point2 = new Point(y=2) +println(point2.y) // выводит 2 +``` + +Что также является хорошей практикой для повышения ясности кода. + +## Скрытые члены и синтаксис Геттер/Сеттер (получатель/установщик значений) +По умолчанию члены класса являются открытыми для внешнего доступа (публичными). Используйте модификатор `private`, чтобы скрыть их от внешнего доступа. +```tut +class Point { + private var _x = 0 + private var _y = 0 + private val bound = 100 + + def x = _x + def x_= (newValue: Int): Unit = { + if (newValue < bound) _x = newValue else printWarning + } + + def y = _y + def y_= (newValue: Int): Unit = { + if (newValue < bound) _y = newValue else printWarning + } + + private def printWarning = println("WARNING: Out of bounds") +} + +val point1 = new Point +point1.x = 99 +point1.y = 101 // выводит предупреждение (printWarning) +``` +В данной версии класса `Point` данные хранятся в скрытых переменных `_x` и `_y`. Существуют методы `def x` и `def y` для доступа к скрытым данным. Методы `def x_=` и `def y_=` (сеттеры) предназначены для проверки и установки значения `_x` и `_y`. Обратите внимание на специальный синтаксис для сеттеров: метод `_=` применяется к имени геттера. + +Первичные параметры конструктора с параметрами `val` и `var` являются общедоступными. Однако, поскольку `val` - это константа, то нельзя писать следующее. +``` +class Point(val x: Int, val y: Int) +val point = new Point(1, 2) +point.x = 3 // <-- не компилируется +``` + +Параметры без `val` или `var` являются скрытыми от внешнего доступа и видимы только внутри класса. +``` +class Point(x: Int, y: Int) +val point = new Point(1, 2) +point.x // <-- не компилируется +``` diff --git a/_ru/tour/compound-types.md b/_ru/tour/compound-types.md new file mode 100644 index 0000000000..4f91d3e75e --- /dev/null +++ b/_ru/tour/compound-types.md @@ -0,0 +1,54 @@ +--- +layout: tour +title: Составные Типы + +discourse: true + +partof: scala-tour + +num: 24 +language: ru +next-page: self-types +previous-page: abstract-type-members + +--- + +Иногда необходимо выразить, то что тип объекта является подтипом нескольких других типов. В Scala это можно выразить с помощью *составных типов*, которые являются объединением нескольких типов объектов. + +Предположим, у нас есть два трейта: `Cloneable` и `Resetable`: + +```tut +trait Cloneable extends java.lang.Cloneable { + override def clone(): Cloneable = { + super.clone().asInstanceOf[Cloneable] + } +} +trait Resetable { + def reset: Unit +} +``` + +Теперь предположим, что мы хотим написать функцию `cloneAndReset`, которая берет объект, клонирует его и сбрасывает (Reset) состояние исходного объекта: + +``` +def cloneAndReset(obj: ?): Cloneable = { + val cloned = obj.clone() + obj.reset + cloned +} +``` + +Возникает вопрос, какой тип параметр `obj` должна принимать наша объедененная функция. Если это `Cloneable`, то объект может использовать метод `clone`, но не `reset`; если это `Resetable` мы можем использовать метод `reset`, но нет операции `clone`. Чтобы избежать приведения типа в такой ситуации, мы можем указать, что тип `obj` является и `Cloneable`, и `Resetable`. Этот совместный тип в Scala записывается как: `Cloneable with Resetable`. + +Вот обновленная функция: + +``` +def cloneAndReset(obj: Cloneable with Resetable): Cloneable = { + //... +} +``` + +Составные типы могут состоять из нескольких типов объектов, и они могут содержать единый доработанный объект, в котором будут доработаны харрактеристики существующих членов объекта. +Общая форма записи: `A with B with C ... { доработанный объект }` + +Пример использования таких доработок приведен на странице об [объединении классов с примесями](mixin-class-composition.html). diff --git a/_ru/tour/default-parameter-values.md b/_ru/tour/default-parameter-values.md new file mode 100644 index 0000000000..7bf61e5430 --- /dev/null +++ b/_ru/tour/default-parameter-values.md @@ -0,0 +1,49 @@ +--- +layout: tour +title: Значения Параметров По умолчанию + +discourse: true + +partof: scala-tour + +num: 33 +language: ru +next-page: named-arguments +previous-page: annotations +prerequisite-knowledge: named-arguments, function syntax + +--- + +Scala предоставляет возможность задавать значения параметров по умолчанию, что позволяет лишний раз не указывать параметры. + +```tut +def log(message: String, level: String = "INFO") = println(s"$level: $message") + +log("System starting") // выведет "INFO: System starting" +log("User not found", "WARNING") // выведет "WARNING: User not found" +``` + +У параметра `level` есть значение по умолчанию, поэтому он необязателен. В последней строке аргумент `"WARNING"` переназначает аргумент по умолчанию `"INFO"`. Вместо того чтоб использовать перегруженные методы в Java, вы можете просто указать дополнительные параметры как параметры по умолчанию для достижения того же эффекта. Однако, если при вызове пропущен хотя бы один аргумент, все остальные аргументы должны вызываться с указанием конкретного имени аргумента. + +```tut +class Point(val x: Double = 0, val y: Double = 0) + +val point1 = new Point(y = 1) +``` +Так мы можем указать что `y = 1`. + +Обратите внимание, что параметры по умолчанию в Scala, при вызове из Java кода, являются обязательными: + +```tut +// Point.scala +class Point(val x: Double = 0, val y: Double = 0) +``` + +```java +// Main.java +public class Main { + public static void main(String[] args) { + Point point = new Point(1); // не скомпилируется + } +} +``` diff --git a/_ru/tour/extractor-objects.md b/_ru/tour/extractor-objects.md new file mode 100644 index 0000000000..46d3ec1805 --- /dev/null +++ b/_ru/tour/extractor-objects.md @@ -0,0 +1,65 @@ +--- +layout: tour +title: Извлекающий Объект + +discourse: true + +partof: scala-tour + +num: 16 +language: ru +next-page: for-comprehensions +previous-page: regular-expression-patterns + +--- + +Извлекающий Объект - это объект с методом `unapply`. В то время как метод `apply` обычно действует как конструктор, который принимает аргументы и создает объект, метод `unapply` действует обратным образом, он принимает объект и пытается вернуть аргументы из которых он (возможно) был создан. Чаще всего этот метод используется в функциях сопоставления с примером и в частично определенных функциях. + +```tut +import scala.util.Random + +object CustomerID { + + def apply(name: String) = s"$name--${Random.nextLong}" + + def unapply(customerID: String): Option[String] = { + val stringArray: Array[String] = customerID.split("--") + if (stringArray.tail.nonEmpty) Some(stringArray.head) else None + } +} + +val customer1ID = CustomerID("Sukyoung") // Sukyoung--23098234908 +customer1ID match { + case CustomerID(name) => println(name) // выведет Sukyoung + case _ => println("Could not extract a CustomerID") +} +``` +Метод `apply` создает `CustomerID` из строки `name`. `unapply` делает обратное, чтобы вернуть `name` обратно. Когда мы вызываем `CustomerID("Sukyoung")`, это сокращенный синтаксис вызова `CustomerID.apply("Sukyoung")`. Когда мы вызываем `case CustomerID(name) => println(name)`, мы на самом деле вызываем метод `unapply`. + +При объявлении нового значения можно использовать пример, в котором значение для инициализации переменной получается через извлечение, используя метод `unapply`. + +```tut +val customer2ID = CustomerID("Nico") +val CustomerID(name) = customer2ID +println(name) // выведет Nico +``` + +Что эквивалентно `val name = CustomerID.unapply(customer2ID).get`. + +```tut +val CustomerID(name2) = "--asdfasdfasdf" +``` + +Если совпадений нет, то бросается `scala.MatchError`: + +```tut:fail +val CustomerID(name3) = "-asdfasdfasdf" +``` + +Возвращаемый тип `unapply` выбирается следующим образом: + +* Если это всего лишь тест, возвращается `Boolean`. Например `case even()`. +* Если в результате найдено одно значение типа `T`, то возвращается `Option[T]`. +* Если вы хотите получить несколько значений `T1,..., Tn`, то ответ необходимо группировать в дополнительный кортеж `Option[(T1,..., Tn)]`. + +Иногда количество извлекаемых значений не является фиксированным. Если в зависимости от входа мы хотим вернуть произвольное количество значений, то для этого случая мы можем определить экстрактор методом `unapplySeq`, который возвращает `Option[Seq[T]]`. Характерным примером такого подхода является разложение `List` с помощью `case List(x, y, z) =>` и разложение `String` с помощью регулярного выражения `Regex`, такого как `case r(name, remainingFields @ _*) =>`. diff --git a/_ru/tour/for-comprehensions.md b/_ru/tour/for-comprehensions.md new file mode 100644 index 0000000000..5845ca23dc --- /dev/null +++ b/_ru/tour/for-comprehensions.md @@ -0,0 +1,66 @@ +--- +layout: tour +title: Придставление вида For + +discourse: true + +partof: scala-tour + +num: 17 +language: ru +next-page: generic-classes +previous-page: extractor-objects + +--- + +Scala предлагает простую запись для выражения *последовательных преобразований*. Такие преобразования представленны в виде for синтаксиса и записывается как `for (enumerators) yield e`, где `enumerators` относятся к списку переислений, разделенных точкой с запятой. Отдельный элемент (*enumerator*) является либо генератором, который вводит новые переменные, либо фильтром. Представление for вычисляет тело `e` (которое связанно с тем что генерирует *enumerator*) и возвращает последовательность вычислений. + +Вот пример: + +```tut +case class User(name: String, age: Int) + +val userBase = List(User("Travis", 28), + User("Kelly", 33), + User("Jennifer", 44), + User("Dennis", 23)) + +val twentySomethings = for (user <- userBase if (user.age >=20 && user.age < 30)) + yield user.name // т. е. добавить результат к списку + +twentySomethings.foreach(name => println(name)) // выводит "Travis Dennis" +``` +Представление `for`, используется с оператором `yield`, на самом деле создает `List`. Потому что мы указали `yield user.name` (тоесть сообщить имя пользователя), получаем `List[String]`. `user <- userBase` и есть наш генератор, а `if (user.age >=20 && user.age < 30)` - это фильтр который отфильтровывает пользователей, не достигших 20-летнего возраста. + +Ниже приведен более сложный пример использования двух генераторов. Он вычисляет все пары чисел между `0` и `n-1`, сумма которых равна заданному значению `v`: + +```tut +def foo(n: Int, v: Int) = + for (i <- 0 until n; + j <- i until n if i + j == v) + yield (i, j) + +foo(10, 10) foreach { + case (i, j) => + println(s"($i, $j) ") // выводит (1, 9) (2, 8) (3, 7) (4, 6) (5, 5) +} + +``` +Здесь `n == 10` и `v == 10`. На первой итерации `i == 0` и `j == 0` так `i + j != v` и поэтому ничего не выдается. `j` увеличивается еще в 9 раз, прежде чем `i` увеличивается до `1`. Без фильтра `if` будет просто напечатано следующее: +``` + +(0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6) (0, 7) (0, 8) (0, 9) (1, 1) ... +``` + +Обратите внимание, что for представление не ограничивается только работой со списками. Каждый тип данных, поддерживающий операции `withFilter`, `map`, and `flatMap` (с соответствующими типами), может быть использован в for представлении. + +Вы можете обойтись без `yield` в for представлении. В таком случае, результатом будет `Unit`. Такое может быть полезным для выполнения кода основанного на побочных эффектах. Вот программа, эквивалентная предыдущей, но без использования `yield`: + +```tut +def foo(n: Int, v: Int) = + for (i <- 0 until n; + j <- i until n if i + j == v) + println(s"($i, $j)") + +foo(10, 10) +``` diff --git a/_ru/tour/generic-classes.md b/_ru/tour/generic-classes.md new file mode 100644 index 0000000000..ab74579e99 --- /dev/null +++ b/_ru/tour/generic-classes.md @@ -0,0 +1,59 @@ +--- +layout: tour +title: Обобщенные Классы + +discourse: true + +partof: scala-tour + +num: 18 +language: ru +next-page: variances +previous-page: for-comprehensions +assumed-knowledge: classes unified-types + +--- +Обобщенные классы - это классы, которые принимают тип в качестве параметра. Они особенно полезны для создания коллекций классов. + +## Объявление обобщенного класса +Обобщенные классы принимают тип в качестве параметра в квадратных скобках `[]`. По соглашению обычно используют буквы `A` в качестве имени параметра типа, хотя можно использовать любое имя. +```tut +class Stack[A] { + private var elements: List[A] = Nil + def push(x: A) { elements = x :: elements } + def peek: A = elements.head + def pop(): A = { + val currentTop = peek + elements = elements.tail + currentTop + } +} +``` +Данная реализация класса `Stack` принимает в качестве параметра любой тип `A`. Это означает что список, `var elements: List[A] = Nil`, может хранить только элементы типа `A`. Процедура `def push` принимает только объекты типа `A` (примечание: `elements = x :: elements` переназначает `elements` в новый список, созданный путем добавления `x`а к текущим `elements`). + +## Использование + +Чтобы использовать обобщенный класс, поместите тип в квадратные скобки вместо `A`. +``` +val stack = new Stack[Int] +stack.push(1) +stack.push(2) +println(stack.pop) // выведет 2 +println(stack.pop) // выведет 1 +``` +Экземпляр `stack` может принимать только Intы. Однако, если тип имеет подтипы, то они также могут быть приняты: +``` +class Fruit +class Apple extends Fruit +class Banana extends Fruit + +val stack = new Stack[Fruit] +val apple = new Apple +val banana = new Banana + +stack.push(apple) +stack.push(banana) +``` +Классы `Apple` и `Banana` наследуются от `Fruit` так, что мы можем засунуть экземпляры `Apple` и `Banana` в пачку `Fruit`. + +_Примечание: подтипы обобщенных типов - *инвариантны*. Это означает, что если у нас есть стэк символов типа `Stack[Char]`, то он не может быть использован как стек интов типа `Stack[Int]`. Это нежелательное поведение, потому как позволило бы нам добавлять в стек символов целые числа. В заключение, `Stack[A]` является подтипом `Stack[B]` тогда и только тогда, когда `B = A`. Поскольку это может быть довольно строгим ограничением, Scala предлагает [механизм вариативного описания параметров типа](variances.html) для контроля за поведением подтипов._ diff --git a/_ru/tour/higher-order-functions.md b/_ru/tour/higher-order-functions.md new file mode 100644 index 0000000000..6df55532e9 --- /dev/null +++ b/_ru/tour/higher-order-functions.md @@ -0,0 +1,109 @@ +--- +layout: tour +title: Функции Высшего Порядка + +discourse: true + +partof: scala-tour + +num: 8 +language: ru +next-page: nested-functions +previous-page: mixin-class-composition + +--- + +Функции высшего порядка могут принимать другие функции в качестве параметров или возвращать функцию в качестве результата +Такое возможно поскольку функции являются объектами первого класса в Scala. +На текущем этапе терминология может казаться немного запутанной, мы используем следующую фразу "функция высшего порядка" как для методов, так и для функций, которые могут принимать другие функции в качестве параметров, или возвращать функции в качестве результата. + +Одним из наиболее распространенных примеров функции высшего порядка +является функция `map`, которая доступна в коллекциях Scala. +```tut +val salaries = Seq(20000, 70000, 40000) +val doubleSalary = (x: Int) => x * 2 +val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000) +``` +`doubleSalary` - это функция, которая принимает один Int `x` и возвращает `x * 2`. В общем случае, кортеж (список имен в скобках) слева от стрелки `=>` - это список параметров, а значение выражения следует справа. Это же значение возвращается в качестве результата. В строке 3 к каждому элементу списка зарплат (salaries) применяется функция `doubleSalary`. + +Чтобы сократить код, мы можем сделать функцию анонимной и передать ее напрямую в качестве аргумента в map: +``` +val salaries = Seq(20000, 70000, 40000) +val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000) +``` +Обратите внимание, что в приведенном выше примере `x`не объявлен как `Int`. Это потому, что компилятор может вывести тип, основываясь на типе который ожидает функция map. Еще более элегантным способом написания этого же кода было бы таким: + +```tut +val salaries = Seq(20000, 70000, 40000) +val newSalaries = salaries.map(_ * 2) +``` +Поскольку компилятор Scala уже знает тип параметров (Int), вам нужно только указать правую часть функции. Единственное условие заключается в том, что вместо имени параметра необходимо использовать `_` (в предыдущем примере это было `x`). + +## Преобразование методов в функции +Также возможно передавать методы в качестве аргументов функциям более высокого порядка, поскольку компилятор Scala может преобразовать метод в функцию. +``` +case class WeeklyWeatherForecast(temperatures: Seq[Double]) { + + private def convertCtoF(temp: Double) = temp * 1.8 + 32 + + def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- передается метод convertCtoF +} +``` +Здесь метод `convertCtoF` передается в `forecastInFahrenheit`. Это возможно, потому что компилятор преобразовывает `convertCtoF` в функцию `x => ConvertCtoF(x)` (примечание: `x` будет сгенерированным именем, которое гарантированно будет уникальным в рамках своей области видимости). + +## Функции, которые принимают функции +Одной из причин использования функций высшего порядка является сокращение избыточного кода. Допустим, вам нужны какие-то методы, которые могли бы повышать чью-то зарплату по разным условиям. Без создания функции высшего порядка это могло бы выглядеть примерно так: + +```tut +object SalaryRaiser { + + def smallPromotion(salaries: List[Double]): List[Double] = + salaries.map(salary => salary * 1.1) + + def greatPromotion(salaries: List[Double]): List[Double] = + salaries.map(salary => salary * math.log(salary)) + + def hugePromotion(salaries: List[Double]): List[Double] = + salaries.map(salary => salary * salary) +} +``` + +Обратите внимание, что каждый из этих трех методов отличается только коэффициентом умножения. Для упрощения можно перенести повторяющийся код в функцию высшего порядка: + +```tut +object SalaryRaiser { + + private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] = + salaries.map(promotionFunction) + + def smallPromotion(salaries: List[Double]): List[Double] = + promotion(salaries, salary => salary * 1.1) + + def bigPromotion(salaries: List[Double]): List[Double] = + promotion(salaries, salary => salary * math.log(salary)) + + def hugePromotion(salaries: List[Double]): List[Double] = + promotion(salaries, salary => salary * salary) +} +``` + +Новый метод, `promotion`, берет зарплату и функцию типа `Double => Double` (т.е. функция, которая берет Double и возвращает Double) и возвращает их произведение. + +## Функции, возвращающие функции + +Есть определенные случаи, когда вы хотите сгенерировать функцию. Вот пример метода, который возвращает функцию. + +```tut +def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = { + val schema = if (ssl) "https://" else "http://" + (endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query" +} + +val domainName = "www.example.com" +def getURL = urlBuilder(ssl=true, domainName) +val endpoint = "users" +val query = "id=1" +val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String +``` + +Обратите внимание, что возвращаемый тип urlBuilder`(String, String) => String`. Это означает, что возвращаемая анонимная функция принимает две строки и возвращает строку. В нашем случае возвращаемая анонимная функция `(endpoint: String, query: String) => s"https://www.example.com/$endpoint?$query"`. \ No newline at end of file diff --git a/_ru/tour/implicit-conversions.md b/_ru/tour/implicit-conversions.md new file mode 100644 index 0000000000..b861d83516 --- /dev/null +++ b/_ru/tour/implicit-conversions.md @@ -0,0 +1,63 @@ +--- +layout: tour +title: Неявные Преобразования + +discourse: true + +partof: scala-tour + +num: 27 +language: ru +next-page: polymorphic-methods +previous-page: implicit-parameters + +--- + +Неявные преобразование типа `S` к типу `T` задается неявным значением функционального типа `S =>T`, или неявным методом, который способен преобразовывать к значению требуемого типа. + +Неявное преобразование применяются в двух случаях: + +* Если выражение `e` типа `S` не подходит под ожидаемый тип выражения `T`. +* Если мы выбирая член `e.m`, где `e` является представителем типа `S`, при этом выбранное имя `m` не найдено среди доступных селекторов пренадлежащих типу `S`. + +В первом случае выполняется поиск приведения `c`, которое можно применить к `e` чтоб тип результата стал соответствовать ожидаемому `T`. +Во втором случае выполняется поиск преобразования `c`, которое применимо к `e` и результат которого бы содержал член с именем `m`. + +Если неявный метод `List[A] => Ordered[List[A]]` находится в области видимости, также как и неявный метод `Int => Ordered[Int]`, то следующая операция с двумя списками типа `List[Int]` является допустимой: + +``` +List(1, 2, 3) <= List(4, 5) +``` + +Неявный метод `Int => Ordered[Int]` предоставляется автоматически через `scala.Predef.intWrapper`. Ниже приведен пример объявления неявного метода `List[A] => Ordered[List[A]]`. + +```tut +import scala.language.implicitConversions + +implicit def list2ordered[A](x: List[A]) + (implicit elem2ordered: A => Ordered[A]): Ordered[List[A]] = + new Ordered[List[A]] { + //заменить на более полезную реализацию + def compare(that: List[A]): Int = 1 + } +``` + +Неявно импортируемый объект `scala.Predef` объявляет ряд псевдонимов для часто используемым типов (например, `scala.collection.immutable.Map` использует псевдоним `Map`) и методов (например, `assert`), а также делает доступным целую серию неявных преобразований. + +Например, при вызове Java метода, который ожидает `java.lang.Integer`, вместо него вы можете свободно использовать `scala.Int`. Потому что Predef включает в себя следующие неявные преобразования: + +```tut +import scala.language.implicitConversions + +implicit def int2Integer(x: Int) = + java.lang.Integer.valueOf(x) +``` + +Компилятор предупреждает при компиляции об обнаружении неявных преобразований, тк неявные преобразования могут иметь разные подводные камни (особенно если использовать их без разбора). + +Чтоб отключить предупреждения выполните одно из следующих действий: + +* Импортируйте `scala.language.implicitConversions` в области видимости где объявленны неявные преобразования. +* Вызовайте компилятора с ключем `-language:implicitConversions`. + +В таком случае при преобразовании компилятором не будет выдаваться никаких предупреждений. diff --git a/_ru/tour/implicit-parameters.md b/_ru/tour/implicit-parameters.md new file mode 100644 index 0000000000..e9f7756e15 --- /dev/null +++ b/_ru/tour/implicit-parameters.md @@ -0,0 +1,73 @@ +--- +layout: tour +title: Неявные Параметры + +discourse: true + +partof: scala-tour + +num: 26 +language: ru +next-page: implicit-conversions +previous-page: self-types + +--- + +Метод может иметь список _неявных_ параметров, помеченный ключевым словом _implicit_ в начале списка параметров. Если параметры в этом списке не передаются как обычно, то Scala будет искать, где можно получить неявное значение требуемого типа, и если найдет, то передаст его автоматически. + +Места, где Scala будет искать эти параметры, делятся на две категории: + +* Скала сначала будет искать неявные параметры, доступ к которым можно получить напрямую (без префикса) в месте вызова метода в котором запрошены неявные параметры. +* Затем он ищет членов, помеченных как implicit во всех объектах компаньонах, связанных с типом неявного параметра. + +Более подробное руководство, о том где scala ищет неявные значения можно найти в [FAQ](//docs.scala-lang.org/tutorials/FAQ/finding-implicits.html) + +В следующем примере мы определяем метод `sum`, который вычисляет сумму элементов списка, используя операции `add` и `unit` моноида. Обратите внимание, что неявные значения не могут находится выше уровнем. + +```tut +abstract class Monoid[A] { + def add(x: A, y: A): A + def unit: A +} + +object ImplicitTest { + implicit val stringMonoid: Monoid[String] = new Monoid[String] { + def add(x: String, y: String): String = x concat y + def unit: String = "" + } + + implicit val intMonoid: Monoid[Int] = new Monoid[Int] { + def add(x: Int, y: Int): Int = x + y + def unit: Int = 0 + } + + def sum[A](xs: List[A])(implicit m: Monoid[A]): A = + if (xs.isEmpty) m.unit + else m.add(xs.head, sum(xs.tail)) + + def main(args: Array[String]): Unit = { + println(sum(List(1, 2, 3))) // использует intMonoid неявно + println(sum(List("a", "b", "c"))) // использует stringMonoid неявно + } +} +``` + +`Monoid` определяет здесь операцию под названием `add`, которая сочетает два элемента типа `A` и возвращает сумму типа `A`, операция `unit` позволяет вернуть отдельный (специфичный) элемент типа `A`. + +Чтобы показать, как работают неявные параметры, сначала определим моноиды `stringMonoid` и `intMonoid` для строк и целых чисел, соответственно. Ключевое слово `implicit` указывает на то, что этот объект может быть использован неявно. + +Метод `sum` принимает `List[A]` и возвращает `A`, который берет начальное `A` из `unit` и объединяет каждое следующее `A` в списке используя `add` метод. Указание параметра `m` в качестве неявного параметра подразумевает, что `xs` параметр будет обеспечен тогда, когда при вызове параметра метода Scala сможет найти неявный `Monoid[A]` чтоб его передать в качестве параметра `m`. + +В нашем `main` методе мы вызываем `sum` дважды и предоставляем только `xs` параметр. Теперь Scala будет искать неявное значение в указанных ранее областях видимости. Первый вызов `sum` проходит с использованием `List[Int]` в качестве `xs`, это означает, что элемент `A` имеет тип `Int`. Неявный список параметров с `m` опущен, поэтому Scala будет искать неявное значение типа `Monoid[Int]`. Первое правило поиска гласит + +> Скала сначала будет искать неявные параметры, доступ к которым можно получить напрямую (без префикса) в месте вызова метода в котором запрошены неявные параметры. + +`intMonoid` - это задание неявного значения, доступ к которому можно получить непосредственно в `main`. Оно имеет подходящий тип, поэтому передается методу `sum` автоматически. + +Второй вызов `sum` проходит используя `List[String]`, что означает, что `A` - это `String`. Неявный поиск будет идти так же, как и в случае с `Int`, но на этот раз будет найден `stringMonoid`, и передан автоматически в качестве `m`. + +Программа выведет на экран +``` +6 +abc +``` diff --git a/_ru/tour/inner-classes.md b/_ru/tour/inner-classes.md new file mode 100644 index 0000000000..2d2445cd0a --- /dev/null +++ b/_ru/tour/inner-classes.md @@ -0,0 +1,81 @@ +--- +layout: tour +title: Внутренние классы + +discourse: true + +partof: scala-tour + +num: 22 +language: ru +next-page: abstract-type-members +previous-page: lower-type-bounds + +--- + +В Scala можно позволить классам иметь в качестве членов другие классы. В отличие от Java-подобных языков, где такие внутренние классы являются членами окружающего класса, в Scala такие внутренние классы привязаны к содержащему его объекту. Предположим, мы хотим, чтобы компилятор не позволял нам на этапе компиляции смешивать узлы этого графа. Для решения этой задачи нам подойдут типы, зависящие от своего расположения. + +Чтобы проиллюстрировать суть подхода, мы быстро набросаем реализацию такого графа: + +```tut +class Graph { + class Node { + var connectedNodes: List[Node] = Nil + def connectTo(node: Node) { + if (connectedNodes.find(node.equals).isEmpty) { + connectedNodes = node :: connectedNodes + } + } + } + var nodes: List[Node] = Nil + def newNode: Node = { + val res = new Node + nodes = res :: nodes + res + } +} +``` +Данная программа представляет собой граф в составленного из списка узлов (`List[Node]`). Каждый узел имеет список других узлов, с которым он связан (`connectedNodes`). Класс `Node` является _зависимым от месторасположения типом_, поскольку он вложен в `Class Graph`. Поэтому все узлы в `connectedNodes` должны быть созданы с использованием `newNode` из одного и тогоже экземпляра `Graph`. + +```tut +val graph1: Graph = new Graph +val node1: graph1.Node = graph1.newNode +val node2: graph1.Node = graph1.newNode +val node3: graph1.Node = graph1.newNode +node1.connectTo(node2) +node3.connectTo(node1) +``` +Мы явно объявили тип `node1`, `node2` и `node3` как `graph1.Node` для ясности, хотя компилятор мог определить это самостоятельно. Это потому, что когда мы вызываем `graph1.newNode`, вызывающий `new Node`, метод использует экземпляр `Node`, специфичный экземпляру `graph1`. + +Если у нас есть два графа, то система типов Scala не позволит смешивать узлы, определенные в рамках одного графа, с узлами другого, так как узлы другого графа имеют другой тип. +Вот некорректная программа: + +``` +val graph1: Graph = new Graph +val node1: graph1.Node = graph1.newNode +val node2: graph1.Node = graph1.newNode +node1.connectTo(node2) // работает +val graph2: Graph = new Graph +val node3: graph2.Node = graph2.newNode +node1.connectTo(node3) // не работает! +``` +Тип `graph1.Node` отличается от типа `graph2.Node`. В Java последняя строка в предыдущем примере программы была бы правильной. Для узлов обоих графов Java будет присваивать один и тот же тип `Graph.Node`, т.е. `Node` имеет префикс класса `Graph`. В Скале такой тип также может быть выражен, он записывается `Graph#Node`. Если мы хотим иметь возможность соединять узлы разных графов, то вам нужно изменить описание первоначальной реализации графов следующим образом: + +```tut +class Graph { + class Node { + var connectedNodes: List[Graph#Node] = Nil + def connectTo(node: Graph#Node) { + if (connectedNodes.find(node.equals).isEmpty) { + connectedNodes = node :: connectedNodes + } + } + } + var nodes: List[Node] = Nil + def newNode: Node = { + val res = new Node + nodes = res :: nodes + res + } +} +``` diff --git a/_ru/tour/lower-type-bounds.md b/_ru/tour/lower-type-bounds.md new file mode 100644 index 0000000000..fcab55a561 --- /dev/null +++ b/_ru/tour/lower-type-bounds.md @@ -0,0 +1,70 @@ +--- +layout: tour +title: Нижнее Ограничение Типов + +discourse: true + +partof: scala-tour + +num: 21 +language: ru +next-page: inner-classes +previous-page: upper-type-bounds +prerequisite-knowledge: upper-type-bounds, generics, variance + +--- + +В то время как [верхнее ограничение типов](upper-type-bounds.html) ограничивают тип до подтипа стороннего типа, *нижнее ограничение типов* объявляют тип супертипом стороннего типа. Термин `B >: A` выражает, что параметр типа `B` или абстрактный тип `B` относится к супертипу типа `A`. В большинстве случаев `A` будет задавать тип класса, а `B` задавать тип метода. + +Вот пример, где это полезно: + +```tut:fail +trait Node[+B] { + def prepend(elem: B): Node[B] +} + +case class ListNode[+B](h: B, t: Node[B]) extends Node[B] { + def prepend(elem: B): ListNode[B] = ListNode(elem, this) + def head: B = h + def tail: Node[B] = t +} + +case class Nil[+B]() extends Node[B] { + def prepend(elem: B): ListNode[B] = ListNode(elem, this) +} +``` + +В данной программе реализован связанный список. `Nil` представляет пустой список. Класс `ListNode` - это узел, который содержит элемент типа `B` (`head`) и ссылку на остальную часть списка (`tail`). Класс `Node` и его подтипы ковариантны, потому что у нас указанно `+B`. + +Однако эта программа _не компилируется_, потому что параметр `elem` в `prepend` имеет тип `B`, который мы объявили *ко*вариантным. Так это не работает, потому что функции *контр*вариантны в типах своих параметров и *ко*вариантны типах в своих результатов. + +Чтобы исправить это, необходимо перевернуть вариантность типа параметра `elem` в `prepend`. Для этого мы вводим новый тип для параметра `U`, у которого тип `B` указан в качестве нижней границы типа. + +```tut +trait Node[+B] { + def prepend[U >: B](elem: U): Node[U] +} + +case class ListNode[+B](h: B, t: Node[B]) extends Node[B] { + def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this) + def head: B = h + def tail: Node[B] = t +} + +case class Nil[+B]() extends Node[B] { + def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this) +} +``` + +Теперь мы можем сделать следующее: +```tut +trait Bird +case class AfricanSwallow() extends Bird +case class EuropeanSwallow() extends Bird + + +val africanSwallowList= ListNode[AfricanSwallow](AfricanSwallow(), Nil()) +val birdList: Node[Bird] = africanSwallowList +birdList.prepend(new EuropeanSwallow) +``` +`Node[Bird]` может быть присвоен `africanSwallowList` , но затем может добавлять и `EuropeanSwallow`. diff --git a/_ru/tour/mixin-class-composition.md b/_ru/tour/mixin-class-composition.md new file mode 100644 index 0000000000..7412c4e863 --- /dev/null +++ b/_ru/tour/mixin-class-composition.md @@ -0,0 +1,83 @@ +--- +layout: tour +title: Композиция классов с примесями + +discourse: true + +partof: scala-tour + +num: 7 +language: ru +next-page: higher-order-functions +previous-page: tuples +prerequisite-knowledge: inheritance, traits, abstract-classes, unified-types + +--- +Примеси - это трейты, которые используются для создания класса. + +```tut +abstract class A { + val message: String +} +class B extends A { + val message = "I'm an instance of class B" +} +trait C extends A { + def loudMessage = message.toUpperCase() +} +class D extends B with C + +val d = new D +println(d.message) // I'm an instance of class B +println(d.loudMessage) // I'M AN INSTANCE OF CLASS B +``` +У класса `D` есть суперкласс `B` и примесь `C`. Классы могут иметь только один суперкласс, но много примисей (используя ключевыое слово `extends` и `with` соответственно). Примеси и суперкласс могут иметь один и тот же супертип. + +Теперь давайте рассмотрим более интересный пример, начиная с абстрактного класса: + +```tut +abstract class AbsIterator { + type T + def hasNext: Boolean + def next(): T +} +``` +Класс имеет абстрактный тип `T` и методы стандартного итератора. + +Далее создаем конкрентную реализацию класса (все абстрактные члены `T`, `hasNext`, и `next` должны быть реализованы): + +```tut +class StringIterator(s: String) extends AbsIterator { + type T = Char + private var i = 0 + def hasNext = i < s.length + def next() = { + val ch = s charAt i + i += 1 + ch + } +} +``` +`StringIterator` принимает `String` и может быть использован для обхода по строке (например, чтоб проверить содержит ли строка определенный символ). + +Теперь давайте создадим трейт который тоже наследуется от `AbsIterator`. + +```tut +trait RichIterator extends AbsIterator { + def foreach(f: T => Unit): Unit = while (hasNext) f(next()) +} +``` +У этого трейта реализован метод `foreach` который постоянно вызывает переданную ему функцию `f: T => Unit` на каждом новом элементе (`next()`) до тех пор пока в итераторе содержатся элементы (`while (hasNext)`). Поскольку `RichIterator` это трейт, ему не нужно реализовывать членов абстрактного класса `AbsIterator`. + +Мы бы хотели объеденить функциональность `StringIterator` и `RichIterator` в один класс. + +```tut +object StringIteratorTest extends App { + class RichStringIter extends StringIterator("Scala") with RichIterator + val richStringIter = new RichStringIter + richStringIter foreach println +} +``` +Новый класс `RichStringIter` включает `StringIterator` как суперкласс и `RichIterator` как примесь. + +Используя только одиночное наследование мы бы не могли добится тогоже уровня гибкости. diff --git a/_ru/tour/multiple-parameter-lists.md b/_ru/tour/multiple-parameter-lists.md new file mode 100644 index 0000000000..77ee1b64fb --- /dev/null +++ b/_ru/tour/multiple-parameter-lists.md @@ -0,0 +1,80 @@ +--- +layout: tour +title: Множественные списки параметров (Каррирование) + +discourse: true + +partof: scala-tour + +num: 10 +language: ru +next-page: case-classes +previous-page: nested-functions + +--- + +Методы могут объявляться с несколькими списками параметров. При этом когда такой метод вызывается с меньшим количеством списков параметров, это приводит к созданию новой функции, которая ожидает на вход не достающий список параметров. Формально это называется [частичное применение](https://en.wikipedia.org/wiki/Partial_application). + +Рассмотрим такие примеры из класса [Traversable](/overviews/collections/trait-traversable.html) коллекции Scala: + +``` +def foldLeft[B](z: B)(op: (B, A) => B): B +``` + +`foldLeft` применяет бинарный оператор `op` к начальному значению `z` и ко всем остальным элементам этого класса слева направо. Ниже приведен пример его использования. + +Начиная с начального значения 0, `foldLeft` применяет функцию `(m, n) => m + n` к каждому элементу списка и предыдущему накопленному значению. + +```tut +val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +val res = numbers.foldLeft(0)((m, n) => m + n) +print(res) // 55 +``` + +Множественные списки параметров имеют избыточный синтаксис, поэтому их следует использоваться экономно. Можем предложить следующие варианты для использования множественных списков (карирования): + +#### Отдельный функциональный параметр + Функцию `op` можно выделить в отдельный функциональный параметр у `foldLeft`, благодаря такому выделению становится возможен более элегантный стиль передачи анонимной функции в метод. Без такого выделения код выглядел бы следующим образом: +``` +numbers.foldLeft(0, {(m: Int, n: Int) => m + n}) +``` + + Обратите внимание, что использование отдельного функционального параметра позволяет нам использовать автоматическое выведение типа для него, что делает код еще более кратким, это было бы невозможно без карирования. + +``` +numbers.foldLeft(0)(_ + _) +``` + Если в утверждении `numbers.foldLeft(0)(_ + _)` зафиксировать отдельный параметр `z`, мы получим частично определенную функцию, которую можно переиспользовать, как показанно ниже: +```tut +val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +val numberFunc = numbers.foldLeft(List[Int]())_ // z = Empty.List[Int] + +val squares = numberFunc((xs, x) => xs:+ x*x) +print(squares.toString()) // List(1, 4, 9, 16, 25, 36, 49, 64, 81, 100) + +val cubes = numberFunc((xs, x) => xs:+ x*x*x) +print(cubes.toString()) // List(1, 8, 27, 64, 125, 216, 343, 512, 729, 1000) +``` + + `foldLeft` и `foldRight` может быть использован в любой из следующих вариаций, +```tut +val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +numbers.foldLeft(0)((sum, item) => sum + item) // Общая Форма +numbers.foldRight(0)((sum, item) => sum + item) // Общая Форма + +numbers.foldLeft(0)(_+_) // Каррированная Форма +numbers.foldRight(0)(_+_) // Каррированная Форма + +(0 /: numbers)(_+_) // Используется вместо foldLeft +(numbers :\ 0)(_+_) // Используется вместо foldRight +``` + + +#### Неявные параметры + Чтоб указать что параметр используется неявно (`implicit`) необходимо задавать несколько списков параметров. Примером может служить следующее: + +``` +def execute(arg: Int)(implicit ec: ExecutionContext) = ??? +``` + diff --git a/_ru/tour/named-arguments.md b/_ru/tour/named-arguments.md new file mode 100644 index 0000000000..138ceb0492 --- /dev/null +++ b/_ru/tour/named-arguments.md @@ -0,0 +1,34 @@ +--- +layout: tour +title: Именованные Аргументы + +discourse: true + +partof: scala-tour + +num: 34 +language: ru +next-page: packages-and-imports +previous-page: default-parameter-values +prerequisite-knowledge: function-syntax + +--- + +При вызове методов можно конкретно указывать название задаваемого аргумента следующим образом: + +```tut +def printName(first: String, last: String): Unit = { + println(first + " " + last) +} + +printName("John", "Smith") // Prints "John Smith" +printName(first = "John", last = "Smith") // Prints "John Smith" +printName(last = "Smith", first = "John") // Prints "John Smith" +``` +Обратите внимание, что при указании имени параметра, порядок аргумента может быть изменен. Однако если какие-то аргументы именованного, а другие нет, то аргументы без имени должны стоять на первом месте и располагаться в том порядке, в котором описанны параметры метода. + +```tut:fail +printName(last = "Smith", "john") // ошибка: позиция после именованного аргумента +``` + +Обратите внимание, что именованные аргументы не работают при вызове Java методов. diff --git a/_ru/tour/nested-functions.md b/_ru/tour/nested-functions.md new file mode 100644 index 0000000000..20698c4387 --- /dev/null +++ b/_ru/tour/nested-functions.md @@ -0,0 +1,38 @@ +--- +layout: tour +title: Вложенные Методы + +discourse: true + +partof: scala-tour + +num: 9 +language: ru +next-page: multiple-parameter-lists +previous-page: higher-order-functions + +--- + +В Scala возможно объявление метода вкладывать в тело другого метода. Это реализованно в следующем примере, в котором метод `factorial` используется для вычисления факториала заданного числа: + +{% scalafiddle %} +```tut + def factorial(x: Int): Int = { + def fact(x: Int, accumulator: Int): Int = { + if (x <= 1) accumulator + else fact(x - 1, x * accumulator) + } + fact(x, 1) + } + + println("Factorial of 2: " + factorial(2)) + println("Factorial of 3: " + factorial(3)) +``` +{% endscalafiddle %} + +Результат выполнения программы: + +``` +Factorial of 2: 2 +Factorial of 3: 6 +``` diff --git a/_ru/tour/operators.md b/_ru/tour/operators.md new file mode 100644 index 0000000000..442fb42705 --- /dev/null +++ b/_ru/tour/operators.md @@ -0,0 +1,81 @@ +--- +layout: tour +title: Операторы + +discourse: true + +partof: scala-tour + +num: 30 +language: ru +next-page: by-name-parameters +previous-page: type-inference +prerequisite-knowledge: case-classes + +--- +В Скале операторы - это обычные методы. В качестве _инфиксного оператора_ может быть использован любой метод с одним параметром. Например, `+` может вызываться с использованием точки: +``` +10.+(1) +``` + +Однако легче воспринимать код когда такие методы записанны как инфиксный оператор: +``` +10 + 1 +``` + +## Создание и использование операторов +В качестве оператора можно использовать любой допустимый символ. Включая имена на подобии `add` или символ (символы) типа `+`. +```tut +case class Vec(val x: Double, val y: Double) { + def +(that: Vec) = new Vec(this.x + that.x, this.y + that.y) +} + +val vector1 = Vec(1.0, 1.0) +val vector2 = Vec(2.0, 2.0) + +val vector3 = vector1 + vector2 +vector3.x // 3.0 +vector3.y // 3.0 +``` +У класса Vec есть метод `+`, который мы использовали для добавления `vector1` и `vector2`. Используя круглые скобки, можно строить сложные выражения с читаемым синтаксисом. Пример создания класса `MyBool`, которое включает в себя методы `and` и `or` + +```tut +case class MyBool(x: Boolean) { + def and(that: MyBool): MyBool = if (x) that else this + def or(that: MyBool): MyBool = if (x) this else that + def negate: MyBool = MyBool(!x) +} +``` + +Теперь можно использовать операторы `and` и `or` в качестве инфиксных операторов: + +```tut +def not(x: MyBool) = x.negate +def xor(x: MyBool, y: MyBool) = (x or y) and not(x and y) +``` + +Это помогает сделать объявление `xor` более читабельным. + +## Порядок очередности +Когда выражение использует несколько операторов, операторы оцениваются на основе приоритета первого символа. Таблица приоритетов символов: +``` +(символы которых нет снизу) +* / % ++ - +: += ! +< > +& +^ +| +(буквы) +``` +Такой приоритет распространяется на любые функции, которые вы задаете. Например, следующее выражение: +``` +a + b ^? c ?^ d less a ==> b | c +``` +эквивалентно +``` +((a + b) ^? (c ?^ d)) less ((a ==> b) | c) +``` +`?^` имеет высший приоритет, потому что начинается с символа `?`. Второй по старшинству приоритет имеет `+`, за которым следуют `==>`, `^?`, `|`, и `less`. diff --git a/_ru/tour/package-objects.md b/_ru/tour/package-objects.md new file mode 100644 index 0000000000..5e51e4a274 --- /dev/null +++ b/_ru/tour/package-objects.md @@ -0,0 +1,72 @@ +--- +layout: tour +title: Объекты Пакета + +discourse: true + +partof: scala-tour + +num: 36 +language: ru +previous-page: packages-and-imports +--- + +# Объекты Пакета + +У каждого пакета может существовать связанный с этим пакетом объект, общий для всех членов пакета. Такой объект может быть только один. Любые выражения, содержащиеся в объекте пакета, считаются членами самого пакета. + +Объекты пакета могут содержать произвольные виды выражений, а не только переменные и методы. Например, они часто используются для хранения псевдонимов типа и наборов неявных преобразований доступных всему пакету. Объекты пакета могут также наследоваться от классов и трейтов Scala. + +По соглашению, исходный код объекта пакета обычно помещается в файл под названием `package.scala`. + +См. пример ниже. Предположим, есть старший класс `Fruit` и три наследуемых от него объекта `Fruit` в пакете. + +`gardening.fruits`: + +``` +// в файле gardening/fruits/Fruit.scala +package gardening.fruits + +case class Fruit(name: String, color: String) +object Apple extends Fruit("Apple", "green") +object Plum extends Fruit("Plum", "blue") +object Banana extends Fruit("Banana", "yellow") +``` + +Теперь предположим, что мы хотим поместить переменную `planted` и метод `showFruit` непосредственно в пакет `gardening`. +Вот как это делается: + +``` +// в файле gardening/fruits/package.scala +package gardening +package object fruits { + val planted = List(Apple, Plum, Banana) + def showFruit(fruit: Fruit): Unit = { + println(s"${fruit.name}s are ${fruit.color}") + } +} +``` + +Для примера, следующий объект `PrintPlanted` импортирует `planted` и `showFruit` точно так же, как с вариантом импорта класса `Fruit`, используя групповой стиль импорта пакета gardening.fruits: + +``` +// в файле PrintPlanted.scala +import gardening.fruits._ +object PrintPlanted { + def main(args: Array[String]): Unit = { + for (fruit <- fruits.planted) { + showFruit(fruit) + } + } +} +``` + +Объекты пакета такиеже как и другие объекты, это означает, что вы можете использовать наследование, при этом сразу нескольких трейтов: + +``` +package object fruits extends FruitAliases with FruitHelpers { + // здесь располагаются вспомогательные классы и переменные +} +``` + +Обратите внимание, что перегрузка метода не работает в объектах пакета. diff --git a/_ru/tour/packages-and-imports.md b/_ru/tour/packages-and-imports.md new file mode 100644 index 0000000000..ed7a0137df --- /dev/null +++ b/_ru/tour/packages-and-imports.md @@ -0,0 +1,86 @@ +--- +layout: tour +title: Пакеты и Импорт + +discourse: true + +partof: scala-tour + +num: 35 +language: ru +previous-page: named-arguments +next-page: package-objects +--- + +# Пакеты и Импорт +Scala использует пакеты для создания пространства имен, которые открывают путь к модульной структуре кода. + +## Создание пакета +Пакеты создаются путем объявления одного или нескольких имен пакетов в верхней части файла Scala. + +``` +package users + +class User +``` +По соглашению пакеты называют тем же именем, что и каталог, содержащий файл Scala. Однако Scala не обращает внимания на расположение файлов. Структура каталогов sbt-проекта для `package users` может выглядеть следующим образом: +``` +- ExampleProject + - build.sbt + - project + - src + - main + - scala + - users + User.scala + UserProfile.scala + UserPreferences.scala + - test +``` +Обратите внимание, что каталог `users` находится внутри каталога `scala` и как в пакете содержатся несколько файлов Scala. Каждый файл Scala в пакете может иметь одно и то же объявление пакета. Другой способ объявления пакетов - с помощью фигурных скобок: +``` +package users { + package administrators { + class NormalUser + } + package normalusers { + class NormalUser + } +} +``` +Как видите, такой способ позволяет вкладывать пакеты друг в друга, а также обеспечивает отличный контроль за областью видимости и возможностью изоляции. + +Имя пакета должно быть все в нижнем регистре, и если код разрабатывается в организации имеющей сайт, то следует использовать имя следующего формата: `<домен-верхнего-уровня>.<доменное-имя>.<название-проекта>`. Например, если бы у Google был проект под названием `SelfDrivingCar`, название пакета выглядело бы следующим образом: +``` +package com.google.selfdrivingcar.camera + +class Lens +``` +Что может соответствовать следующей структуре каталога: `SelfDrivingCar/src/main/scala/com/google/selfdrivingcar/camera/Lens.scala`. + +## Импорт +Указание `import` открывает доступ к членам (классам, признакам, функциям и т.д.) в других пакетах. Указание `import` не требуется для доступа к членам одного и того же пакета. Указание `import` избирательны: +``` +import users._ // групповой импорт всего пакета users +import users.User // импортировать только User +import users.{User, UserPreferences} // импортировать только User, UserPreferences +import users.{UserPreferences => UPrefs} // импортировать и переименовать +``` + +Одним из отличий Scala от Java является то, что импорт можно использовать где угодно: + +```tut +def sqrtplus1(x: Int) = { + import scala.math.sqrt + sqrt(x) + 1.0 +} +``` +В случае возникновения конфликта имен и необходимости импортировать что-либо из корня проекта, имя пакета должно начинаться с префикса `_root_`: +``` +package accounts + +import _root_.users._ +``` + + +Примечание: Пакеты `scala` и `java.lang`, а также `object Predef` импортируются по умолчанию. diff --git a/_ru/tour/pattern-matching.md b/_ru/tour/pattern-matching.md new file mode 100644 index 0000000000..28466af962 --- /dev/null +++ b/_ru/tour/pattern-matching.md @@ -0,0 +1,151 @@ +--- +layout: tour +title: Сопоставление с примером + +discourse: true + +partof: scala-tour + +num: 12 +language: ru +next-page: singleton-objects +previous-page: case-classes +prerequisite-knowledge: case-classes, string-interpolation, subtyping + +--- + +Сопоставление с примером - это механизм сравнения значений с определенным примером. При успешном совпадении значение может быть разложено на составные части. Мы рассматриваем сопоставление с примером, как более мощную версию `switch` оператора из Java. Eго также можно использовать вместо серии if/else выражений. + +## Синтаксис +Сопоставляющее выражение имеет значение, ключевое слово `match` и по крайней мере, один пункт `case`. +```tut +import scala.util.Random + +val x: Int = Random.nextInt(10) + +x match { + case 0 => "zero" + case 1 => "one" + case 2 => "two" + case _ => "many" +} +``` +Значение константы `x` выше представляет собой случайное целое число от 0 до 10. `x` становится левым операндом оператора `match`, а справа - выражением с четырьмя образцами (называемые еще _вариантами_). Последний вариант `_` - позволяет "поймать все оставшиеся варианты" т. е. для любого числа больше 2. + +Сопоставление с примером возвращает значение. +```tut +def matchTest(x: Int): String = x match { + case 1 => "one" + case 2 => "two" + case _ => "many" +} +matchTest(3) // many +matchTest(1) // one +``` +Это сопоставляющее выражение имеет тип String, так как все варианты сопоставления возвращают String. Поэтому функция `matchTest` возвращает String. + +## Сопоставление с классами образцами + +Классы образцы особенно полезны при сопоставление с примером. + +```tut +abstract class Notification + +case class Email(sender: String, title: String, body: String) extends Notification + +case class SMS(caller: String, message: String) extends Notification + +case class VoiceRecording(contactName: String, link: String) extends Notification + + +``` +`Notification` - абстрактный суперкласс, от которого наследуются три конкретных типа реализаций классов образцов `Email`, `SMS`, и `VoiceRecording`. Теперь мы можем делать сопоставление с примером используя в качестве примера один из этих классов образцов. +При сопоставлении с классом обазцом мы можем сразу извлекать параметры из которых состоит класс (благодаря автоматическому использованию [объекта распаковщика](extractor-objects.html)): + +``` +def showNotification(notification: Notification): String = { + notification match { + case Email(email, title, _) => + s"You got an email from $email with title: $title" + case SMS(number, message) => + s"You got an SMS from $number! Message: $message" + case VoiceRecording(name, link) => + s"you received a Voice Recording from $name! Click the link to hear it: $link" + } +} +val someSms = SMS("12345", "Are you there?") +val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123") + +println(showNotification(someSms)) // выводит "You got an SMS from 12345! Message: Are you there?" + +println(showNotification(someVoiceRecording)) // выводит "you received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123" +``` +Функция `showNotification` принимает в качестве параметра абстрактный тип `Notification` который проверяет по образцам (т.е. выясняет, является ли он классом `Email`, `SMS` или `VoiceRecording`). В `case Email(email, title, _)`поля `email` и `title` используются в возвращаемом значении, а вот поле `body` игнорируется благодаря символу `_`. + +## Ограждения примеров +Ограждения примеров - это просто логические выражения, которые используются для того, чтобы сделать выбор более специфичным. Просто добавьте `if <булевское выражение>` после примера. +``` + +def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = { + notification match { + case Email(email, _, _) if importantPeopleInfo.contains(email) => + "You got an email from special someone!" + case SMS(number, _) if importantPeopleInfo.contains(number) => + "You got an SMS from special someone!" + case other => + showNotification(other) // в этом варианте считается подходящими параметры любого типа. Значит этот вариант выполняется во всех случаях и передает исходный параметр в функцию showNotification + } +} + +val importantPeopleInfo = Seq("867-5309", "jenny@gmail.com") + +val someSms = SMS("867-5309", "Are you there?") +val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123") +val importantEmail = Email("jenny@gmail.com", "Drinks tonight?", "I'm free after 5!") +val importantSms = SMS("867-5309", "I'm here! Where are you?") + +println(showImportantNotification(someSms, importantPeopleInfo)) +println(showImportantNotification(someVoiceRecording, importantPeopleInfo)) +println(showImportantNotification(importantEmail, importantPeopleInfo)) +println(showImportantNotification(importantSms, importantPeopleInfo)) +``` + +В варианте `case Email(email, _, _) if importantPeopleInfo.contains(email)`, пример сравнивается только если `email` находится в списке `importantPeopleInfo`. + +## Сопоставление только с типом +Вы можете сопоставлять только по типу как в примере: +```tut +abstract class Device +case class Phone(model: String) extends Device{ + def screenOff = "Turning screen off" +} +case class Computer(model: String) extends Device { + def screenSaverOn = "Turning screen saver on..." +} + +def goIdle(device: Device) = device match { + case p: Phone => p.screenOff + case c: Computer => c.screenSaverOn +} +``` +метод `goIdle` реализует изменение поведения в зависимости от типа `Device`. По соглашению в качестве названия варианта используется первая буква типа (в данном случае `p` и `c`). + +## Запечатанные классы +Трейты и классы могут быть помечены как `sealed` это означает, что подтипы должны быть объявлены в одном файле, гарантируя тем самым, что все подтипы будут известны. + +```tut +sealed abstract class Furniture +case class Couch() extends Furniture +case class Chair() extends Furniture + +def findPlaceToSit(piece: Furniture): String = piece match { + case a: Couch => "Lie on the couch" + case b: Chair => "Sit on the chair" +} +``` +Это полезно для сопоставления с примером, ведь мы будем заранее знать все доступные варианты и нам не нужен вариант "все остальные". + +## Замечания + +Сопоставление с примером наиболее полезно для сопоставления алгебраических типов, выраженных через [классы образцы](case-classes.html). +Scala также позволяет создавать образцы независимо от классов образцов, через использование метода `unapply` в [извлекающих объектах](extractor-objects.html). diff --git a/_ru/tour/polymorphic-methods.md b/_ru/tour/polymorphic-methods.md new file mode 100644 index 0000000000..5d696cb7bc --- /dev/null +++ b/_ru/tour/polymorphic-methods.md @@ -0,0 +1,36 @@ +--- +layout: tour +title: Полиморфные методы + +discourse: true + +partof: scala-tour + +num: 28 +language: ru +next-page: type-inference +previous-page: implicit-conversions +prerequisite-knowledge: unified-types + +--- + +Также как и у обобщенных классов, у методов есть полиморфизм по типу, с таким же синтаксисом (параметр типа указывается в квадратных скобках сразу после названия метода). + +Вот пример: + +```tut +def listOfDuplicates[A](x: A, length: Int): List[A] = { + if (length < 1) + Nil + else + x :: listOfDuplicates(x, length - 1) +} +println(listOfDuplicates[Int](3, 4)) // List(3, 3, 3, 3) +println(listOfDuplicates("La", 8)) // List(La, La, La, La, La, La, La, La) +``` + +Метод `listOfDuplicates` принимает параметр типа `A` и параметры значений `x` и `length`. Значение `x` имеет тип `A`. Если `length < 1` мы возвращаем пустой список. В противном случае мы добавляем `x`к списку, которые возвращаем через рекурсивный вызовов. (Обратите внимание, что `::` означает добавление элемента слева к списку справа). + +В первом вызове метода мы явно указываем параметр типа, записывая `[Int]`. Поэтому первым аргументом должен быть `Int` и тип возвращаемого значения будет `List[Int]`. + +Во втором вызове показанно, что вам не всегда нужно явно указывать параметр типа. Часто компилятор сам может вывести тип исходя из контекста или типа передаваемых аргументов. В этом варианте `"La"` - это `String`, поэтому компилятор знает, что `A` должен быть `String`. diff --git a/_ru/tour/regular-expression-patterns.md b/_ru/tour/regular-expression-patterns.md new file mode 100644 index 0000000000..8b3c138744 --- /dev/null +++ b/_ru/tour/regular-expression-patterns.md @@ -0,0 +1,61 @@ +--- +layout: tour +title: Регулярные Выражения + +discourse: true + +partof: scala-tour + +num: 15 +language: ru +next-page: extractor-objects +previous-page: singleton-objects + +--- + +Регулярные выражения - это строки, которые задают шаблон для поиска данных. Любая строка может быть преобразована в регулярное выражение методом `.r`. + +```tut +import scala.util.matching.Regex + +val numberPattern: Regex = "[0-9]".r + +numberPattern.findFirstMatchIn("awesomepassword") match { + case Some(_) => println("Password OK") + case None => println("Password must contain a number") +} +``` + +В приведенном выше примере `numberPattern` - это `Regex` (регулярное выражение), которое мы используем, чтобы убедиться, что пароль содержит число. + +Используя круглые скобки можно объеденять сразу несколько групп регулярных выражений. + +```tut +import scala.util.matching.Regex + +val keyValPattern: Regex = "([0-9a-zA-Z-#() ]+): ([0-9a-zA-Z-#() ]+)".r + +val input: String = + """background-color: #A03300; + |background-image: url(img/header100.png); + |background-position: top center; + |background-repeat: repeat-x; + |background-size: 2160px 108px; + |margin: 0; + |height: 108px; + |width: 100%;""".stripMargin + +for (patternMatch <- keyValPattern.findAllMatchIn(input)) + println(s"key: ${patternMatch.group(1)} value: ${patternMatch.group(2)}") +``` +Здесь мы обработали сразу и ключи и значения строки. В каждой совпадении есть подгруппа совпадений. Вот как выглядит результат: +``` +key: background-color value: #A03300 +key: background-image value: url(img +key: background-position value: top center +key: background-repeat value: repeat-x +key: background-size value: 2160px 108px +key: margin value: 0 +key: height value: 108px +key: width value: 100 +``` diff --git a/_ru/tour/self-types.md b/_ru/tour/self-types.md new file mode 100644 index 0000000000..10a6539ca1 --- /dev/null +++ b/_ru/tour/self-types.md @@ -0,0 +1,40 @@ +--- +layout: tour +title: Самоописываемый тип + +discourse: true + +partof: scala-tour + +num: 25 +language: ru +next-page: implicit-parameters +previous-page: compound-types +topics: self-types +prerequisite-knowledge: nested-classes, mixin-class-composition + +--- +Самоописываемый тип - это способ объявить, что трейт должен быть смешан с другим трейтом, даже если он не расширяет его напрямую. Что открывает доступ к членам зависимости без импортирования. + +Самоописываемый тип - это способ сузить тип `this` или другого идентификатора, который ссылается на `this`. Синтаксис похож на синтаксис обычной функции, но означает кое-что иное. + +Чтобы использовать самоописываемый тип в трейте, напишите идентификатор, тип другого трейта который хотите добавить и `=>` (например, `someIdentifier: SomeOtherTrait =>`). +```tut +trait User { + def username: String +} + +trait Tweeter { + this: User => // переназначил this + def tweet(tweetText: String) = println(s"$username: $tweetText") +} + +class VerifiedTweeter(val username_ : String) extends Tweeter with User { // Мы добавили User потому этого требует Tweeter + def username = s"real $username_" +} + +val realBeyoncé = new VerifiedTweeter("Beyoncé") +realBeyoncé.tweet("Just spilled my glass of lemonade") // выведет "real Beyoncé: Just spilled my glass of lemonade" +``` + +Поскольку мы указали `this: User =>` в трейте `Tweeter`, теперь переменная `username` находится в пределах видимости для метода `tweet`. Это также означает что `VerifiedTweeter` при наследовании от `Tweeter` должен быть смешан с `User` (используя `with User`). diff --git a/_ru/tour/singleton-objects.md b/_ru/tour/singleton-objects.md new file mode 100644 index 0000000000..0bc752d1f2 --- /dev/null +++ b/_ru/tour/singleton-objects.md @@ -0,0 +1,107 @@ +--- +layout: tour +title: Одноэлементные Объекты + +discourse: true + +partof: scala-tour + +num: 13 +language: ru +next-page: regular-expression-patterns +previous-page: pattern-matching +prerequisite-knowledge: classes, methods, private-methods, packages, option +--- +Объект - это класс, существующий в единственном экземпляре. Он создается лениво, когда на него ссылаются, также как ленивые значения (lazy val). + +На самом верхнем уровне объект является одноэлементным. + +Как член класса или как локальная переменная, он ведет себя точно так же как ленивое значение (lazy val). +# Объявление одноэлементного объекта +Объект - это значение. Объявление объекта происходит похожим с классом образом, но используется ключевое слово `object`: +```tut +object Box +``` + +Вот пример объекта с методом: +``` +package logging + +object Logger { + def info(message: String): Unit = println(s"INFO: $message") +} +``` +Метод `info` может быть импортирован из любой точки программы. Создание подобных методов является распространенным вариантом использования одноэлементных объектов. + +Давайте посмотрим, как использовать `info` в другом пакете: + +``` +import logging.Logger.info + +class Project(name: String, daysToComplete: Int) + +class Test { + val project1 = new Project("TPS Reports", 1) + val project2 = new Project("Website redesign", 5) + info("Created projects") // Prints "INFO: Created projects" +} +``` + +Метод `info` виден благодаря указанию импорта `import logging.Logger.info`. + +Замечание: Если `object` не является объектом верхнего уровня, но вложен в другой класс или объект, то объект, как и любой другой член, "зависим от пути". Это означает, что при двух видах напитков, `class Milk` and `class OrangeJuice`, член класса `object NutritionInfo` "зависит" от окружающего класса, либо milk либо orange juice. `milk.NutritionInfo` полностью отделен от `orangejuice.NutritionInfo`. + +## Объекты компаньоны + +Объект с тем же именем, что и класс, называется _объект компаньон_. И наоборот, класс является классом-компаньоном объекта. Класс или объект компаньон может получить доступ к приватным членам своего спутника. Используйте объект компаньон для методов и значений, которые не специфичны для экземпляров класса компаньона. +``` +import scala.math._ + +case class Circle(radius: Double) { + import Circle._ + def area: Double = calculateArea(radius) +} + +object Circle { + private def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0) +} + +val circle1 = new Circle(5.0) + +circle1.area +``` + +Класс `Circle` имеет член `area`, который специфичен для каждого экземпляра, и одноэлементный `object Circle` с методом `calculateArea`, который доступен для каждого экземпляра. + +Объект компаньон может также содержать методы производящие экземпляры класса спутника: +```tut +class Email(val username: String, val domainName: String) + +object Email { + def fromString(emailString: String): Option[Email] = { + emailString.split('@') match { + case Array(a, b) => Some(new Email(a, b)) + case _ => None + } + } +} + +val scalaCenterEmail = Email.fromString("scala.center@epfl.ch") +scalaCenterEmail match { + case Some(email) => println( + s"""Registered an email + |Username: ${email.username} + |Domain name: ${email.domainName} + """) + case None => println("Error: could not parse email") +} +``` +`object Email` содержит производящий метод `fromString`, который создает экземпляр `Email` из строки. Мы возвращаем результат как опцию `Option[Email]` на случай возникновения ошибок парсинга. + +Примечание: Если у класса или объекта есть компаньон, они должны быть размещены в одном и том же файле. Чтобы задать компаньонов в REPL, либо определите их на той же строке, либо перейдите в режим `:paste`. + +## Примечания для Java-программистов ## + +`static` члены в Java смоделированы как обычные члены объекта компаньона в Scala. + +При использовании объекта компаньона из Java-кода, члены будут определены в сопутствующем классе компаньоне с `static` модификатором. Это называется _пробрасывание статики_. Такое происходит, даже если вы сами не определили класс компаньон. diff --git a/_ru/tour/tour-of-scala.md b/_ru/tour/tour-of-scala.md new file mode 100644 index 0000000000..4c982672ff --- /dev/null +++ b/_ru/tour/tour-of-scala.md @@ -0,0 +1,64 @@ +--- +layout: tour +title: Введение + +discourse: true + +partof: scala-tour + +num: 1 +language: ru +next-page: basics +--- + +## Добро пожаловать к обзору +Здесь вы увидите вводное описание наиболее часто используемых возможностей Scala. +Этот обзор предназначен для новичков в изучении языка. + +Это всего лишь небольшая экскурсия, а не полный курс освоения языка. Для глубокого погружения рекомендуем почитать [книги](/books.html) или воспользоваться курсами +[на других ресурсах](/learn.html). + +## Что такое Scala? +Scala - это современный мультипарадигмальный язык программирования, разработанный для выражения общих концепций программирования в простой, удобной и типобезопасной манере. Элегантно обьединяя особенности объектно-ориентированных и функциональных языков. + +## Scala объектно ориентированный ## +Scala - это чистый объектно-ориентированный язык в том смысле, что [каждое значение - это объект](unified-types.html). Типы и поведение объектов описаны в [классах](classes.html) и [трейтах](traits.html)(признаках). Классы расширяются за счет подклассификации и гибкого [смешивания классов](mixin-class-composition.html), который используется как механизм замены множественного наследования. + +## Scala функциональный ## +Scala также является функциональным языком в том смысле, что [каждая функция - это значение](unified-types.html). Scala предоставляет [легкий синтаксис](basics.html) для определения анонимных функций, поддерживает [функции высшего порядка](higher-order-functions.html), позволяет функциям быть [вложенными](nested-functions.html) и поддерживает [каррирование](multiple-parameter-lists.html). [Классы образцы](case-classes.html) Scala имеют встроенную поддержку [сопоставления с примером](pattern-matching.html) для создания алгебраических типов данных, которые используются в большинстве функциональных языках программирования. [Объекты одиночки (сингэлтоны)](singleton-objects.html) предоставляют удобный способ группировки функций, не входящих в класс. + +Кроме того, понятие Scala о сопоставлении с примером естественно распространяется на [обработку XML-данных](https://github.com/scala/scala-xml/wiki/XML-Processing) с помощью [шаблонов последовательного проектирования](regular-expression-patterns.html), через расширение [объектов распаковщиков](extractor-objects.html). В этом отношении [for представление](for-comprehensions.html) очень удобно использовать для создания запросов. Такие особенности делают Scala идеальным решением для разработки приложений по типу веб-сервисов. + +## Scala статически типизированый ## +Scala оснащен выразительной системой типов, которая обеспечивает безопасное и гармоничное использование абстракций. В частности, система типов поддерживает: + +* [обобщенные классы](generic-classes.html) +* [вариантность типов](variances.html) +* [верхние](upper-type-bounds.html) и [нижние](lower-type-bounds.html) границы типов +* [внутренние классы](inner-classes.html) и [члены абстрактного типа](abstract-type-members.html), как часть объектов +* [составные типы](compound-types.html) +* [самоописываемые типы](self-types.html) +* [неявные параметры](implicit-parameters.html) и [неявные преобразования](implicit-conversions.html) +* [полиморфные методы](polymorphic-methods.html) + +[Выведение типов](type-inference.html) означает, что пользователю не обязательно описывать код избыточной информацией о типах. +Такой функционал обеспечивает мощную основу для безопасного переиспользования программных абстракций и типобезопасного расширения программного обеспечения. + +## Scala расширяемый ## + +Обычно разработка специфичных для предметной области приложений требует специфичных для этой предметной области языковых возможностей. Scala предоставляет уникальную комбинацию механизмов, которые позволяют легко добавлять новые языковые конструкции в виде библиотек. + +Во многих случаях это можно сделать без использования средств мета-программирования, таких как макросы. Например: + +* [Неявные классы](http://docs.scala-lang.org/overviews/core/implicit-classes.html) позволяет добавлять новые методы к уже существующим типам. +* [Интерполяция строк](/overviews/core/string-interpolation.html) расширяется пользователем с помощью пользовательских интерполяторов. + +## Scala совместимый + +Scala полностью совместим с популярной средой Java Runtime Environment (JRE). Взаимодействие с основным объектно-ориентированным языком программирования Java происходит максимально гладко. Более новые функции Java, такие как SAM, [лямбды](higher-order-functions.html), [аннотации](annotations.html) и [дженерики](generic-classes.html), имеют прямые аналоги в Scala. + +Те функции Scala, которые не имеют аналогов в Java, такие как [параметры по умолчанию](default-parameter-values.html) и [именованные параметры](named-arguments.html), компилируются как можно ближе к Java. Scala имеет такую же компиляционную модель (отдельная компиляция, динамическая загрузка классов), как у Java и позволяет получить доступ к тысячам уже существующих высококачественных библиотек. + +## Наслаждайтесь туром! + +Предлагаю перейти на [следующую страницу](basics.html) нашего тура, дабы продолжить знакомство. \ No newline at end of file diff --git a/_ru/tour/traits.md b/_ru/tour/traits.md new file mode 100644 index 0000000000..0d029af770 --- /dev/null +++ b/_ru/tour/traits.md @@ -0,0 +1,84 @@ +--- +layout: tour +title: Трейты + +discourse: true + +partof: scala-tour + +num: 5 +language: ru +next-page: tuples +previous-page: classes +topics: traits +prerequisite-knowledge: expressions, classes, generics, objects, companion-objects + +--- + +Трейты используются чтоб обмениваться между классами информацией о структуре и полях. Они похожи на интерфейсы из Java 8. Классы и объекты могут расширять трейты, но трейты не могут быть созданны и поэтому не имеют параметров. + +## Объявление трейта +Минимальное объявление трейта - это просто ключевое слово `trait` и его имя: + +```tut +trait HairColor +``` + +Трейты наиболее полезны в качестве обобщенного типа с абстрактными методами. +```tut +trait Iterator[A] { + def hasNext: Boolean + def next(): A +} +``` + +При наследовании от трейта `Iterator[A]` требует указание типа `A` а также реализация методов `hasNext` и `next`. + +## Использование трейтов +Чтоб использовать трейты, необходимо наследовать класс от него используя ключевое слово `extends`. Затем необходимо реализовать все абстрактные члены трейта, используя ключевое слово `override`: +```tut +trait Iterator[A] { + def hasNext: Boolean + def next(): A +} + +class IntIterator(to: Int) extends Iterator[Int] { + private var current = 0 + override def hasNext: Boolean = current < to + override def next(): Int = { + if (hasNext) { + val t = current + current += 1 + t + } else 0 + } +} + + +val iterator = new IntIterator(10) +iterator.next() // вернет 0 +iterator.next() // вернет 1 +``` +Этот класс `IntIterator` использует параметр `to` в качестве верхней границы. Он наследуется от `Iterator[Int]`, что означает, что метод `next` должен возвращать Int. + +## Подтипы +Туда где требуется определенный тип трейта, мы можем передавать любой наследованный от требуемого трейта класс +```tut +import scala.collection.mutable.ArrayBuffer + +trait Pet { + val name: String +} + +class Cat(val name: String) extends Pet +class Dog(val name: String) extends Pet + +val dog = new Dog("Harry") +val cat = new Cat("Sally") + +val animals = ArrayBuffer.empty[Pet] +animals.append(dog) +animals.append(cat) +animals.foreach(pet => println(pet.name)) // выведет "Harry" и "Sally" +``` +У трейта `Pet` есть абстрактное поле `name`, которое реализованно в классах `Cat` and `Dog`. В последней строке мы вызываем `pet.name`, который должен быть реализован в любом подтипе унаследованным от трейта `Pet`. diff --git a/_ru/tour/tuples.md b/_ru/tour/tuples.md new file mode 100644 index 0000000000..59e9206cff --- /dev/null +++ b/_ru/tour/tuples.md @@ -0,0 +1,95 @@ +--- +layout: tour +title: Кортеж + +discourse: true + +partof: scala-tour + +num: 6 +language: ru +next-page: mixin-class-composition +previous-page: traits +topics: tuples + +--- + +В Scala, a кортеж это класс контейнер содержащий упорядоченный набор элементов различного типа. +Кортежи неизменеямы. + +Кортежи могут пригодиться, когда нам нужно вернуть сразу несколько значений из функции. + +Кортеж может быть создан как: + +```tut +val ingredient = ("Sugar" , 25):Tuple2[String, Int] +``` +Такая запись создает кортеж размерности 2, содержащий пару элементов String и Int. + +Кортежи в Скале - представленны серией классов: Tuple2, Tuple3 и т.д., до Tuple22. +Таким образом, создавая кортеж с n элементами (n лежащими между 2 и 22), Скала просто создает один из соответствующих классов, который параметризован типом входящих в состав элементов. + +В нашем примере, составляющие тип Tuple2[String, Int]. + +## Доступ к элементам + +Доступ к элементам кортежа осуществляется при помощи синтаксиса подчеркивания. +'tuple._n' дает n-ый элемент (до тех пор, сколько существует элементов). + +```tut +println(ingredient._1) // Sugar + +println(ingredient._2) // 25 +``` + +## Разборка данных кортежа + +Scala кортежи также поддерживают разборку. + +```tut +val (name, quantity) = ingredient + +println(name) // Sugar + +println(quantity) // 25 +``` + +Разборка данных кортежа может быть использованна в [сопоставлении с примером](pattern-matching.html) + +```tut +val planetDistanceFromSun = List(("Mercury", 57.9), ("Venus", 108.2), ("Earth", 149.6 ), ("Mars", 227.9), ("Jupiter", 778.3)) + +planetDistanceFromSun.foreach{ tuple => { + + tuple match { + + case ("Mercury", distance) => println(s"Mercury is $distance millions km far from Sun") + + case p if(p._1 == "Venus") => println(s"Venus is ${p._2} millions km far from Sun") + + case p if(p._1 == "Earth") => println(s"Blue planet is ${p._2} millions km far from Sun") + + case _ => println("Too far....") + + } + + } + +} +``` + +Или в ['for' представлении](for-comprehensions.html). + +```tut +val numPairs = List((2, 5), (3, -7), (20, 56)) + +for ((a, b) <- numPairs) { + + println(a * b) + +} +``` + +Значение () типа Unit по свой сути совпадает со значением () типа Tuple0. Может быть только одно значение такого типа, так как в нём нет элементов. + +Иногда бывает трудно выбирать между кортежами и классами образцами. Как правило, классы образцы являются предпочтительным выбором, если сам класс-контейнер содержащий элементы представляет собой значимый смысл. diff --git a/_ru/tour/type-inference.md b/_ru/tour/type-inference.md new file mode 100644 index 0000000000..3f936385fa --- /dev/null +++ b/_ru/tour/type-inference.md @@ -0,0 +1,75 @@ +--- +layout: tour +title: Выведение Типа + +discourse: true + +partof: scala-tour + +num: 29 +language: ru +next-page: operators +previous-page: polymorphic-methods +--- + +Компилятор Scala часто может вывести тип выражения, так что вам не нужно указывать его явным образом. + +## Не указывая тип + +```tut +val businessName = "Montreux Jazz Café" +``` +Компилятор может определить, что тип константы `businessName` является `String`. Аналогичным образом это работает и для методов: + +```tut +def squareOf(x: Int) = x * x +``` +Компилятор может определить, что возвращаемый тип является `Int`, поэтому явного указания типа не требуется. + +Для рекурсивных методов компилятор не в состоянии вывести тип. Вот программа, которая не скомпилируется по этой причине: + +```tut:fail +def fac(n: Int) = if (n == 0) 1 else n * fac(n - 1) +``` + +Также необязательно указывать параметры типа при вызове [полиморфных методов](polymorphic-methods.html) или [обобщенных классов](generic-classes.html). Компилятор Scala определит тип параметра из контекста и из типов фактически передаваемых параметров метода/конструктора. + +Вот два примера: + +```tut +case class MyPair[A, B](x: A, y: B); +val p = MyPair(1, "scala") // тип: MyPair[Int, String] + +def id[T](x: T) = x +val q = id(1) // тип: Int +``` + +Компилятор использует типы аргументов `MyPair` для определения типа `A` и `B`. Тоже самое для типа `x`. + +## Параметры + +Для параметров компилятор никогда не выводит тип. Однако, в некоторых случаях, он может вывести типы для параметров анонимной функции при передаче ее в качестве аргумента. + +```tut +Seq(1, 3, 4).map(x => x * 2) // List(2, 6, 8) +``` + +Параметр у map - `f: A => B` (функциональный параметр переводящий тип из A в B). Поскольку мы разместили целые числа в нашей последовательности `Seq`, компилятор знает, что элемент `A` является `Int` (т.е. `x` является целым числом). Поэтому компилятор может определить из выражения `x * 2`, что результат (`B`) является типом `Int`. + +## Когда _не следует_ полагаться на выведение типа + +Обычно считается, наиболее удобочитаемым объявить тип членов, которые открыты для публичного использования через API. Поэтому мы рекомендуем вам явно указывать тип для любых API, которые будут доступны пользователям вашего кода. + +Кроме того, выведение может иногда приводить к слишком специфичному типу. Предположим, мы напишем: + +```tut +var obj = null +``` + +Тогда мы не сможем далее сделать это переназначение: + +```tut:fail +obj = new AnyRef +``` + +Такое не будет компилироваться, потому что для `obj` предполагался тип `Null`. Поскольку единственным значением этого типа является `null`, то невозможно присвоить другое значение. \ No newline at end of file diff --git a/_ru/tour/unified-types.md b/_ru/tour/unified-types.md new file mode 100644 index 0000000000..222a84d492 --- /dev/null +++ b/_ru/tour/unified-types.md @@ -0,0 +1,82 @@ +--- +layout: tour +title: Единобразие типов + +discourse: true + +partof: scala-tour + +num: 3 +language: ru +next-page: classes +previous-page: basics +prerequisite-knowledge: classes, basics + +--- + +В Scala все значения имеют тип, включая числовые значения и функции. Диаграмма ниже иллюстрирует подмножество иерархии типов. + +Scala Type Hierarchy + +## Иерархия типов Scala ## + +[`Any`](http://www.scala-lang.org/api/2.12.1/scala/Any.html) это супертип всех типов, также называемый верхним типом. Он определяет несколько универсальных методов, таких как `equals`, `hashCode` и `toString`. У `Any` есть два прямых подкласса: `AnyVal` и `AnyRef`. + +`AnyVal` представляет числовые типы. Существует девять предварительно определенных числовых типов и они никогда не могут быть равны 'null': `Double`, `Float`, `Long`, `Int`, `Short`, `Byte`, `Char`, `Unit`, и `Boolean`. `Unit` - это числовой тип, который не содержит значимой информации (также обозначает пустое множество). Есть только один представитель типа `Unit`, который можно объявить вот так: `()`. Все функции должны возвращать что-то, поэтому иногда `Unit` полезный для возврата тип. + +`AnyRef` представляет ссылочные типы. Все типы, не относящиеся к "типам значений", называются ссылочными типами. Каждый объявлялемый пользователем тип в Scala является подтипом `AnyRef`. Если Scala исходить из контекста среды исполнения Java, `AnyRef` соответствует `java.lang.Object`. + +Вот пример, демонстрирующий, что строки, целые числа, символы, логические значения и функции являются объектами, как и любой другой объект: + +```tut +val list: List[Any] = List( + "a string", + 732, // целое число + 'c', // символ + true, // логическое значение + () => "анонимная функция возвращающая строку" +) + +list.foreach(element => println(element)) +``` + +Объявляем переменную `list` типа `List[Any]`. Список инициализируется элементами различных типов, но все они являются экземпляром `scala.Any`, так что вы можете добавить их в список. + +Ниже приведен вывод программы: + +``` +a string +732 +c +true + +``` + +## Приведение типа +Числовые типы могут быть приведены следующим образом: +Scala Type Hierarchy + +Например: + +```tut +val x: Long = 987654321 +val y: Float = x // 9.8765434E8 (заметьте, что некоторая точность теряется в этом случае.) + +val face: Char = '☺' +val number: Int = face // 9786 +``` + +Приведение типа - однонаправленно. Следующий пример не скомпилируется: + +``` +val x: Long = 987654321 +val y: Float = x // 9.8765434E8 +val z: Long = y // Не подходит +``` + +Вы также можете приводить к своему подтипу. Об этом мы поговорим позже в ходе нашего обзора. + +## Nothing и Null +`Nothing` является подтипом всех типов, также называемым нижним типом. Нет значения, которое имеет тип `Nothing`. Обычно он используется чтоб дать сигнал о невычислимости, например брошено исключение, выход из программы, бесконечное зацикливание (т.е. это тип выражения, которое не вычисляется). + +`Null` подтип всех ссылочных типов (т.е. любой подтип AnyRef). Он имеет одно значение, определяемое ключевым словом литерала `null`. `Null` предоставляется в основном для функциональной совместимости с другими языками JVM и почти никогда не должен использоваться в коде Scala. Об альтернативах `null` мы поговорим позднее. diff --git a/_ru/tour/upper-type-bounds.md b/_ru/tour/upper-type-bounds.md new file mode 100644 index 0000000000..c4e6717c8a --- /dev/null +++ b/_ru/tour/upper-type-bounds.md @@ -0,0 +1,54 @@ +--- +layout: tour +title: Верхнее Ограничение Типа + +discourse: true + +partof: scala-tour +categories: tour +num: 20 +language: ru +next-page: lower-type-bounds +previous-page: variances + +--- + +В Скале [параметры типа](generic-classes.html) и [члены абстрактного типа](abstract-type-members.html) могут быть ограничены определенными диапазонами. Такие диапазоны ограничивают конкретные значение типа и, возможно, предоставляют больше информации о членах таких типов. _Верхнее ограничение типа_ `T <: A` указывает на то что тип `T` относится к подтипу типа `A`. +Приведем пример, демонстрирующий верхнее ограничение типа для типа класса `PetContainer`: + +```tut +abstract class Animal { + def name: String +} + +abstract class Pet extends Animal {} + +class Cat extends Pet { + override def name: String = "Cat" +} + +class Dog extends Pet { + override def name: String = "Dog" +} + +class Lion extends Animal { + override def name: String = "Lion" +} + +class PetContainer[P <: Pet](p: P) { + def pet: P = p +} + +val dogContainer = new PetContainer[Dog](new Dog) +val catContainer = new PetContainer[Cat](new Cat) +``` + +```tut:fail +// это не скомпилируется +val lionContainer = new PetContainer[Lion](new Lion) +``` +Класс `PetContainer` принимает тип `P`, который должен быть подтипом `Pet`. `Dog` и `Cat` - это подтипы `Pet`, поэтому мы можем создать новые `PetContainer[Dog]` и `PetContainer[Cat]`. Однако, если мы попытаемся создать `PetContainer[Lion]`, то получим следующую ошибку: + +`type arguments [Lion] do not conform to class PetContainer's type parameter bounds [P <: Pet]` + +Это потому, что `Lion` не является подтипом `Pet`. diff --git a/_ru/tour/variances.md b/_ru/tour/variances.md new file mode 100644 index 0000000000..8e6a774522 --- /dev/null +++ b/_ru/tour/variances.md @@ -0,0 +1,154 @@ +--- +layout: tour +title: Вариантность + +discourse: true + +partof: scala-tour + +num: 19 +language: ru +next-page: upper-type-bounds +previous-page: generic-classes + +--- + +Вариантность - это указание определенной специфики взаимосвязи между сложними связанными типам. Скала поддерживает вариантную аннотацую типов у [обобщенных классов](generic-classes.html), что позволяет им быть ковариантными, контрвариантными или инвариантными (если нет никакого указание на вариантность). Использование вариантности в системе типов позволяет устанавливать понятные взаимосвязи между сложными типами, в то время как отсутствие вариантности может ограничить повторное использование абстракции класса. + +```tut +class Foo[+A] // ковариантный класс +class Bar[-A] // контрвариантный класс +class Baz[A] // инвариантными класс +``` + +### Ковариантность + +Параметр типа `A` обобщенного класса можно сделать ковариантным с помощью аннотации `+A`. Для некоторого класса `List[+A]`, указание `A` в виде коварианта подразумевает, что для двух типов `A` и `B`, где `A` является подтипом `B`, `List[A]` представляет собой подтип `List[B]`. Что позволяет нам создавать очень полезные и интуитивно понятные взаимоотношения между типами с использованием дженериков. + +Рассмотрим простую структуру классов: + +```tut +abstract class Animal { + def name: String +} +case class Cat(name: String) extends Animal +case class Dog(name: String) extends Animal +``` + +И `Cat` и `Dog` являются подтипами `Animal`. Стандартная библиотека Scala имеет обобщенный неизменяемый тип `List[+A]`, где параметр типа `A` является ковариантным. Это означает, что `List[Cat]` - это `List[Animal]`, а `List[Dog]` - это также `List[Animal]`. Интуитивно понятно, что список кошек и список собак - это список животных, и вы должны быть в состоянии заменить любого из них на `List[Animal]`. + +В следующем примере метод `printAnimalNames` принимает в качестве аргумента список животных и выводит их имена в новой строке. Если бы `List[A]` не был ковариантным, последние два вызова метода не компилировались бы, что сильно ограничило бы полезность метода `printAnimalNames`. + +```tut +object CovarianceTest extends App { + def printAnimalNames(animals: List[Animal]): Unit = { + animals.foreach { animal => + println(animal.name) + } + } + + val cats: List[Cat] = List(Cat("Whiskers"), Cat("Tom")) + val dogs: List[Dog] = List(Dog("Fido"), Dog("Rex")) + + printAnimalNames(cats) + // Whiskers + // Tom + + printAnimalNames(dogs) + // Fido + // Rex +} +``` + +### Контрвариантность + +Параметр типа `A` обобщенного класса можно сделать контрвариантным с помощью аннотации `-A`. Это создает схожее, но противоположное ковариационному, взаимоотношения между типом параметра и подтипами класса. То есть, для некого класса `Writer[-A]`, указание `A` контрвариантным подразумевает, что для двух типов `A` и `B` где `A` является подтипом `B`, `Writer[B]` является подтипом `Writer[A]`. + +Рассмотрим классы `Cat`, `Dog`, и `Animal`, описанные выше для следующего примера: + +```tut +abstract class Printer[-A] { + def print(value: A): Unit +} +``` + +`Printer[A]` - это простой класс, который знает, как распечатать некоторый тип `A`. Давайте определим подклассы для конкретных типов: + +```tut +class AnimalPrinter extends Printer[Animal] { + def print(animal: Animal): Unit = + println("The animal's name is: " + animal.name) +} + +class CatPrinter extends Printer[Cat] { + def print(cat: Cat): Unit = + println("The cat's name is: " + cat.name) +} +``` + +Если `Printer[Cat]` знает, как распечатать любой класс `Cat` в консоли, а `Printer[Animal]` знает, как напечатать любое `Animal` в консоли, то разумно если `Printer[Animal]` также знает, как напечатать любое `Cat`. Обратного отношения нет, потому что `Printer[Cat]` не знает, как распечатать любой `Animal` на консоли. Чтоб иметь возможность заменить `Printer[Animal]` на `Printer[Cat]`, необоходимо `Printer[A]` сделать контрвариантным. + +```tut +object ContravarianceTest extends App { + val myCat: Cat = Cat("Boots") + + def printMyCat(printer: Printer[Cat]): Unit = { + printer.print(myCat) + } + + val catPrinter: Printer[Cat] = new CatPrinter + val animalPrinter: Printer[Animal] = new AnimalPrinter + + printMyCat(catPrinter) + printMyCat(animalPrinter) +} +``` + +Результатом работы этой программы будет: + +``` +The cat's name is: Boots +The animal's name is: Boots +``` + +### Инвариантность + +Обобщенные классы в Scala по умолчанию являются инвариантными. Это означает, что они не являются ни ковариантными, ни контрвариантными друг другу. В контексте следующего примера класс `Container` является инвариантным. Между `Container[Cat]` и `Container[Animal]`, нет ни прямой и ни обратной взаимосвязи. + +```tut +class Container[A](value: A) { + private var _value: A = value + def getValue: A = _value + def setValue(value: A): Unit = { + _value = value + } +} +``` + +Может показаться что `Container[Cat]` должен также являтся и `Container[Animal]`, но позволить мутабельному обобщеному классу быть ковариантным было бы небезопасно. В данном примере очень важно, чтобы `Container` был инвариантным. Предположим, что `Container` на самом деле был ковариантным, что-то вроде этого могло случиться: + +``` +val catContainer: Container[Cat] = new Container(Cat("Felix")) +val animalContainer: Container[Animal] = catContainer +animalContainer.setValue(Dog("Spot")) +val cat: Cat = catContainer.getValue // Ой, мы бы закончили собакой присвоеной к коту. +``` + +К счастью, компилятор остановит нас прежде, чем мы зайдем так далеко. + +### Другие Примеры + +Другим примером, который может помочь понять вариантность, является трейт `Function1[-T, +R]` из стандартной библиотеки Scala. `Function1` представляет собой функцию с одним параметром, где первый тип `T` представляет собой параметр типа, а второй тип `R` представляет собой тип результата. Функция `Function1` является контрвариантной в рамках типа принимаемого аргумента, а ковариантной - в рамках возвращаемого типа. Для этого примера мы будем использовать явное обозначение типа `A =>B` чтоб продемонстрировать `Function1[A, B]`. + +Рассмотрим схожий пример `Cat`, `Dog`, `Animal` в тойже взаимосвязи что и раньше, плюс следующее: + +```tut +abstract class SmallAnimal extends Animal +case class Mouse(name: String) extends SmallAnimal +``` + +Предположим, мы работаем с функциями, которые принимают типы животных и возвращают типы еды, которую они едят. Если мы хотим `Cat => SmallAnimal` (потому что кошки едят маленьких животных), но вместо этого мы получим функцию `Animal => Mouse`, то наша программа все равно будет работать. Интуитивно функция `Animal => Mouse` все равно будет принимать `Cat` в качестве аргумента, тк `Cat` является `Animal`, и возвращать `Mouse` - который также является и `SmallAnimal`. Поскольку мы можем безопасно заменить первое вторым, можно сказать, что `Animal => Mouse` аналогично `Cat => SmallAnimal`. + +### Сравнение с другими языками + +В языках, похожих на Scala, разные способы поддержи вариантности. Например, указания вариантности в Скале очень похожи на то как это делается в C#, где такие указания добавляются при объявлении абстракции класса (вариантность при объявлении). В Java, однако, указание вариантности задается клиентами класса непосредственно при использовании абстракции класса (вариантность при использовании). From 52ba93366c7ae92131b63add7716f16c1640c7a0 Mon Sep 17 00:00:00 2001 From: "Kotobotov.ru" Date: Sun, 27 Jan 2019 20:28:44 +0700 Subject: [PATCH 2/5] :ok_hand: Updating code due to code review changes. Took 1 hour 46 minutes --- _ru/tour/abstract-type-members.md | 6 +++--- _ru/tour/annotations.md | 2 +- _ru/tour/automatic-closures.md | 4 ++-- _ru/tour/basics.md | 16 ++++++++-------- _ru/tour/by-name-parameters.md | 2 +- _ru/tour/classes.md | 2 +- _ru/tour/compound-types.md | 4 ++-- _ru/tour/for-comprehensions.md | 6 +++--- _ru/tour/implicit-conversions.md | 6 +++--- _ru/tour/inner-classes.md | 4 ++-- _ru/tour/mixin-class-composition.md | 8 ++++---- _ru/tour/multiple-parameter-lists.md | 10 +++++----- _ru/tour/named-arguments.md | 2 +- _ru/tour/nested-functions.md | 2 +- _ru/tour/operators.md | 2 +- _ru/tour/package-objects.md | 2 +- _ru/tour/pattern-matching.md | 4 ++-- _ru/tour/polymorphic-methods.md | 2 +- _ru/tour/regular-expression-patterns.md | 2 +- _ru/tour/self-types.md | 2 +- _ru/tour/tour-of-scala.md | 6 +++--- _ru/tour/traits.md | 4 ++-- _ru/tour/tuples.md | 8 ++++---- _ru/tour/unified-types.md | 4 ++-- _ru/tour/variances.md | 10 +++++----- 25 files changed, 60 insertions(+), 60 deletions(-) diff --git a/_ru/tour/abstract-type-members.md b/_ru/tour/abstract-type-members.md index be69a91a93..1fd14d1e3c 100644 --- a/_ru/tour/abstract-type-members.md +++ b/_ru/tour/abstract-type-members.md @@ -15,7 +15,7 @@ prerequisite-knowledge: variance, upper-type-bound --- Абстрактные типы, такие как трейты и абстрактные классы, которые могут содержать членов абстрактного типа. -Это означает, что только непосредственно реализация определяет, каким именно будет этот тип. +Это означает, что только конкретный экземпляр определяет, каким именно будет этот тип. Вот пример: ```tut @@ -24,7 +24,7 @@ trait Buffer { val element: T } ``` -Здесь мы определили абстрактный вариант тип `T`, который используется для описания типа `element`. Мы можем расширить его в абстрактном классе, добавив верхнюю границу связанного с `T` типа, чтобы сделать его более определенным. +Здесь мы определили абстрактный вариант тип `T`, который используется для описания типа `element`. Мы можем расширить его в абстрактном классе, добавив верхнюю границу связанного с `T` типа, чтобы сделать его более конкретным. ```tut abstract class SeqBuffer extends Buffer { @@ -33,7 +33,7 @@ abstract class SeqBuffer extends Buffer { def length = element.length } ``` -Обратите внимание, как мы можем использовать еще один абстрактный абстрактный тип `type U` в качестве верхней границы типа. Класс `SeqBuffer` позволяет хранить в буфере только последовательности, указывая, что тип `T` должен быть подтипом `Seq[U]` для нового абстрактного типа `U`. +Обратите внимание, как мы можем использовать еще один абстрактный тип `type U` в качестве верхней границы типа. Класс `SeqBuffer` позволяет хранить в буфере только последовательности, указывая, что тип `T` должен быть подтипом `Seq[U]` для нового абстрактного типа `U`. [Трейты](traits.html) или [классы](classes.html) с абстрактными членами типа часто используются в сочетании с анонимными экземплярами классов. Чтобы проиллюстрировать это рассмотрим программу, которая имеет дело с буфером, который ссылается на список целых чисел: diff --git a/_ru/tour/annotations.md b/_ru/tour/annotations.md index c97c226854..d15436162c 100644 --- a/_ru/tour/annotations.md +++ b/_ru/tour/annotations.md @@ -105,7 +105,7 @@ class MyScalaClass ... public class MyClass extends HisClass ... ``` -В такомже случае Scala предоставляет такую же возможность +В этом случае Scala предоставляет такую же возможность ``` @SourceURL("http://coders.com/") diff --git a/_ru/tour/automatic-closures.md b/_ru/tour/automatic-closures.md index 057788c251..7de83279d8 100644 --- a/_ru/tour/automatic-closures.md +++ b/_ru/tour/automatic-closures.md @@ -8,7 +8,7 @@ partof: scala-tour num: 14 --- -Scala допускает использование в качестве параметров методов имена безпараметрических функций. При вызове такого метода фактические параметры для безпараметрических функций не вычисляются, а передается функция с нулем аргументов, которая захватывает вычисление соответствующего параметра (так называемый *вызов по имени*). +Scala допускает использование в качестве параметров методов имена беспараметрических функций. При вызове такого метода фактические параметры для беспараметрических функций не вычисляются, а передается функция с нулем аргументов, которая захватывает вычисление соответствующего параметра (так называемый *вызов по имени*). Следующий код демонстрирует этот механизм: @@ -25,7 +25,7 @@ Scala допускает использование в качестве пара } } -Функция whileLoop принимает два параметра `cond` и `body`. При использовании функции значения этих параметров не вычисляются. Но всякий раз, когда параметры используются в теле `whileLoop`, их значение будет вычислятся заново через использование автоматически созданных неявно вызываемых функций. Таким образом, наш метод `whileLoop` реализует Java-подобный цикл while-loop со схемой рекурсивной реализации. +Функция whileLoop принимает два параметра `cond` и `body`. При использовании функции значения этих параметров не вычисляются. Но всякий раз, когда параметры используются в теле `whileLoop`, их значение будет вычисляться заново через использование автоматически созданных неявно вызываемых функций. Таким образом, наш метод `whileLoop` реализует Java-подобный цикл while-loop со схемой рекурсивной реализации. Мы можем комбинировать использование [инфиксных/постфиксных операторов](operators.html) с этим механизмом для создания более сложных выражений (с хорошим синтаксисом). diff --git a/_ru/tour/basics.md b/_ru/tour/basics.md index 70bbc6b0cb..78550faec5 100644 --- a/_ru/tour/basics.md +++ b/_ru/tour/basics.md @@ -57,7 +57,7 @@ println(x) // 2 Названные результаты, такие как `x` в примере, называются значениями. Вызов значения не приводит к его повторному вычислению. -Значения константны и не могут быть переназначены. +Значения не изменяемы и не могут быть переназначены. ```tut:fail x = 3 // Не компилируется. @@ -105,7 +105,7 @@ println({ Функции - это выражения, которые принимают параметры. -Вы можете определить анонимную функцию (т.е. без имени), которая возвращает переданное число прибавив к нему еденицу: +Вы можете определить анонимную функцию (т.е. без имени), которая возвращает переданное число прибавив к нему единицу: ```tut (x: Int) => x + 1 @@ -178,7 +178,7 @@ def getSquareString(input: Double): String = { square.toString } ``` -Последнее выражениее в теле становится возвращаемым значением метода. (У Scala есть ключевое слово `return` , но оно практически не используется.) +Последнее выражение в теле становится возвращаемым значением метода. (У Scala есть ключевое слово `return` , но оно практически не используется.) ## Классы @@ -190,7 +190,7 @@ class Greeter(prefix: String, suffix: String) { println(prefix + name + suffix) } ``` -Возвращаемый тип метода `greet` это `Unit`, используется тогда, когда не имеет смысла что-либо возвращать. Аналогично `void` в Java и C. (Поскольку каждое выражение Scala должно иметь какое-то значение, поэтому, при оттуствии возвращающегося значения, возвращается одиночка (сингэлтон) типа Unit. Явным образом его можно задаеть как `()`, он не несет какой-либо информации.) +Возвращаемый тип метода `greet` это `Unit`, используется тогда, когда не имеет смысла что-либо возвращать. Аналогично `void` в Java и C. (Поскольку каждое выражение Scala должно иметь какое-то значение, поэтому, при отсутствии возвращающегося значения, возвращается одиночка (сингэлтон) типа Unit. Явным образом его можно задать как `()`, оно не несет какой-либо информации.) Вы можете создать экземпляр класса используя ключевое слово `new`. @@ -224,7 +224,7 @@ if (point == anotherPoint) { println(point + " and " + anotherPoint + " are the same.") } else { println(point + " and " + anotherPoint + " are different.") -} // Point(1,2) и Point(1,2) одни и теже. +} // Point(1,2) и Point(1,2) одни и те же. if (point == yetAnotherPoint) { println(point + " and " + yetAnotherPoint + " are the same.") @@ -264,7 +264,7 @@ println(newerId) // 2 ## Трейты -Трейты - как типы описывают харрактеристики классов, в нем могут объявляться определенные поля и методы. Трейты можно комбинировать. +Трейты - как типы описывают характеристики классов, в нем могут объявляться определенные поля и методы. Трейты можно комбинировать. Объявить трейт можно с помощью ключевого слова `trait`. @@ -303,14 +303,14 @@ customGreeter.greet("Scala developer") // How are you, Scala developer? ``` {% endscalafiddle %} -Здесь `DefaultGreeter` наследуется только от одиного трейта, но можно наследоваться от нескольких. +Здесь `DefaultGreeter` наследуется только от одного трейта, но можно наследоваться от нескольких. Позже мы рассмотрим трейты [подробнее](traits.html). ## Главный метод Главный метод является отправной точкой в программе. -Для Виртуальной Java Машины требуется, чтобы главный метод назывался `main` и принимал один аргумент, массив строк. +Для Виртуальной Машины Java требуется, чтобы главный метод назывался `main` и принимал один аргумент, массив строк. Используя объект, можно задать главный метод следующим образом: diff --git a/_ru/tour/by-name-parameters.md b/_ru/tour/by-name-parameters.md index 653f211585..9cd798b2ee 100644 --- a/_ru/tour/by-name-parameters.md +++ b/_ru/tour/by-name-parameters.md @@ -37,6 +37,6 @@ whileLoop (i > 0) { ``` Метод `whileLoop` использует несколько списков параметров - условие и тело цикла. Если `condition` является верным, выполняется `body`, а затем выполняется рекурсивный вызов whileLoop. Если `condition` является ложным, то тело никогда не вычисляется, тк у нас стоит `=>` перед типом `body`. -Теперь, когда мы передаем `i > 0` как наше условие `condition` и `println(i); i-= 1` как тело `body`, код ведет себя также как обычный цикл в большинстве языков программировния. +Теперь, когда мы передаем `i > 0` как наше условие `condition` и `println(i); i-= 1` как тело `body`, код ведет себя также как обычный цикл в большинстве языков программирования. Такая возможность откладывать вычисления параметра до его использования может помочь повысить производительность, отсекая не нужные вычисления при определенных условиях. diff --git a/_ru/tour/classes.md b/_ru/tour/classes.md index 82201cd3a6..0b5a7e0bec 100644 --- a/_ru/tour/classes.md +++ b/_ru/tour/classes.md @@ -59,7 +59,7 @@ println(point1.x) // выводит 1 ``` -В этой версии класса `Point`, `x` и `y` имеют значение по умолчанию `0`, поэтому аргументов не требуется. Однако, поскольку конструктор считывает аргументы слева направо, если вы просто хотите передать значение `y`, то вам нужно будет указать завадаемый параметр. +В этой версии класса `Point`, `x` и `y` имеют значение по умолчанию `0`, поэтому аргументов не требуется. Однако, поскольку конструктор считывает аргументы слева направо, если вы просто хотите передать значение `y`, то вам нужно будет указать задаваемый параметр. ``` class Point(var x: Int = 0, var y: Int = 0) val point2 = new Point(y=2) diff --git a/_ru/tour/compound-types.md b/_ru/tour/compound-types.md index 4f91d3e75e..988900cab4 100644 --- a/_ru/tour/compound-types.md +++ b/_ru/tour/compound-types.md @@ -38,7 +38,7 @@ def cloneAndReset(obj: ?): Cloneable = { } ``` -Возникает вопрос, какой тип параметр `obj` должна принимать наша объедененная функция. Если это `Cloneable`, то объект может использовать метод `clone`, но не `reset`; если это `Resetable` мы можем использовать метод `reset`, но нет операции `clone`. Чтобы избежать приведения типа в такой ситуации, мы можем указать, что тип `obj` является и `Cloneable`, и `Resetable`. Этот совместный тип в Scala записывается как: `Cloneable with Resetable`. +Возникает вопрос, какой тип параметр `obj` должна принимать наша объединённая функция. Если это `Cloneable`, то объект может использовать метод `clone`, но не `reset`; если это `Resetable` мы можем использовать метод `reset`, но нет операции `clone`. Чтобы избежать приведения типа в такой ситуации, мы можем указать, что тип `obj` является и `Cloneable`, и `Resetable`. Этот совместный тип в Scala записывается как: `Cloneable with Resetable`. Вот обновленная функция: @@ -48,7 +48,7 @@ def cloneAndReset(obj: Cloneable with Resetable): Cloneable = { } ``` -Составные типы могут состоять из нескольких типов объектов, и они могут содержать единый доработанный объект, в котором будут доработаны харрактеристики существующих членов объекта. +Составные типы могут состоять из нескольких типов объектов, и они могут содержать единый доработанный объект, в котором будут доработаны характеристики существующих членов объекта. Общая форма записи: `A with B with C ... { доработанный объект }` Пример использования таких доработок приведен на странице об [объединении классов с примесями](mixin-class-composition.html). diff --git a/_ru/tour/for-comprehensions.md b/_ru/tour/for-comprehensions.md index 5845ca23dc..6c825e2354 100644 --- a/_ru/tour/for-comprehensions.md +++ b/_ru/tour/for-comprehensions.md @@ -1,6 +1,6 @@ --- layout: tour -title: Придставление вида For +title: Представление вида For discourse: true @@ -13,7 +13,7 @@ previous-page: extractor-objects --- -Scala предлагает простую запись для выражения *последовательных преобразований*. Такие преобразования представленны в виде for синтаксиса и записывается как `for (enumerators) yield e`, где `enumerators` относятся к списку переислений, разделенных точкой с запятой. Отдельный элемент (*enumerator*) является либо генератором, который вводит новые переменные, либо фильтром. Представление for вычисляет тело `e` (которое связанно с тем что генерирует *enumerator*) и возвращает последовательность вычислений. +Scala предлагает простую запись для выражения *последовательных преобразований*. Такие преобразования представлены в виде `for синтаксиса` и записывается как `for (enumerators) yield e`, где `enumerators` относятся к списку перечислений, разделенных точкой с запятой. Отдельный элемент (*enumerator*) является либо генератором, который вводит новые переменные, либо фильтром. Представление for вычисляет тело `e` (которое связанно с тем что генерирует *enumerator*) и возвращает последовательность вычислений. Вот пример: @@ -30,7 +30,7 @@ val twentySomethings = for (user <- userBase if (user.age >=20 && user.age < 30) twentySomethings.foreach(name => println(name)) // выводит "Travis Dennis" ``` -Представление `for`, используется с оператором `yield`, на самом деле создает `List`. Потому что мы указали `yield user.name` (тоесть сообщить имя пользователя), получаем `List[String]`. `user <- userBase` и есть наш генератор, а `if (user.age >=20 && user.age < 30)` - это фильтр который отфильтровывает пользователей, не достигших 20-летнего возраста. +Представление `for`, используется с оператором `yield`, на самом деле создает `List`. Потому что мы указали `yield user.name` (то есть вывести имя пользователя), получаем `List[String]`. `user <- userBase` и есть наш генератор, а `if (user.age >=20 && user.age < 30)` - это фильтр который отфильтровывает пользователей, не достигших 20-летнего возраста. Ниже приведен более сложный пример использования двух генераторов. Он вычисляет все пары чисел между `0` и `n-1`, сумма которых равна заданному значению `v`: diff --git a/_ru/tour/implicit-conversions.md b/_ru/tour/implicit-conversions.md index b861d83516..1040f47f9b 100644 --- a/_ru/tour/implicit-conversions.md +++ b/_ru/tour/implicit-conversions.md @@ -18,7 +18,7 @@ previous-page: implicit-parameters Неявное преобразование применяются в двух случаях: * Если выражение `e` типа `S` не подходит под ожидаемый тип выражения `T`. -* Если мы выбирая член `e.m`, где `e` является представителем типа `S`, при этом выбранное имя `m` не найдено среди доступных селекторов пренадлежащих типу `S`. +* Если мы выбирая член `e.m`, где `e` является представителем типа `S`, при этом выбранное имя `m` не найдено среди доступных селекторов принадлежащих типу `S`. В первом случае выполняется поиск приведения `c`, которое можно применить к `e` чтоб тип результата стал соответствовать ожидаемому `T`. Во втором случае выполняется поиск преобразования `c`, которое применимо к `e` и результат которого бы содержал член с именем `m`. @@ -57,7 +57,7 @@ implicit def int2Integer(x: Int) = Чтоб отключить предупреждения выполните одно из следующих действий: -* Импортируйте `scala.language.implicitConversions` в области видимости где объявленны неявные преобразования. -* Вызовайте компилятора с ключем `-language:implicitConversions`. +* Импортируйте `scala.language.implicitConversions` в области видимости, где объявлены неявные преобразования. +* Вызывайте компилятор с ключом `-language:implicitConversions`. В таком случае при преобразовании компилятором не будет выдаваться никаких предупреждений. diff --git a/_ru/tour/inner-classes.md b/_ru/tour/inner-classes.md index 2d2445cd0a..64fca34b24 100644 --- a/_ru/tour/inner-classes.md +++ b/_ru/tour/inner-classes.md @@ -13,7 +13,7 @@ previous-page: lower-type-bounds --- -В Scala можно позволить классам иметь в качестве членов другие классы. В отличие от Java-подобных языков, где такие внутренние классы являются членами окружающего класса, в Scala такие внутренние классы привязаны к содержащему его объекту. Предположим, мы хотим, чтобы компилятор не позволял нам на этапе компиляции смешивать узлы этого графа. Для решения этой задачи нам подойдут типы, зависящие от своего расположения. +В Scala классам можно иметь в качестве членов другие классы. В отличие от Java-подобных языков, где такие внутренние классы являются членами окружающего класса, в Scala такие внутренние классы привязаны к содержащему его объекту. Предположим, мы хотим, чтобы компилятор не позволял нам на этапе компиляции смешивать узлы этого графа. Для решения этой задачи нам подойдут типы, зависящие от своего расположения. Чтобы проиллюстрировать суть подхода, мы быстро набросаем реализацию такого графа: @@ -35,7 +35,7 @@ class Graph { } } ``` -Данная программа представляет собой граф в составленного из списка узлов (`List[Node]`). Каждый узел имеет список других узлов, с которым он связан (`connectedNodes`). Класс `Node` является _зависимым от месторасположения типом_, поскольку он вложен в `Class Graph`. Поэтому все узлы в `connectedNodes` должны быть созданы с использованием `newNode` из одного и тогоже экземпляра `Graph`. +Данная программа представляет собой граф в составленного из списка узлов (`List[Node]`). Каждый узел имеет список других узлов, с которым он связан (`connectedNodes`). Класс `Node` является _зависимым от месторасположения типом_, поскольку он вложен в `Class Graph`. Поэтому все узлы в `connectedNodes` должны быть созданы с использованием `newNode` из одного и того же экземпляра `Graph`. ```tut val graph1: Graph = new Graph diff --git a/_ru/tour/mixin-class-composition.md b/_ru/tour/mixin-class-composition.md index 7412c4e863..06c982cc26 100644 --- a/_ru/tour/mixin-class-composition.md +++ b/_ru/tour/mixin-class-composition.md @@ -31,7 +31,7 @@ val d = new D println(d.message) // I'm an instance of class B println(d.loudMessage) // I'M AN INSTANCE OF CLASS B ``` -У класса `D` есть суперкласс `B` и примесь `C`. Классы могут иметь только один суперкласс, но много примисей (используя ключевыое слово `extends` и `with` соответственно). Примеси и суперкласс могут иметь один и тот же супертип. +У класса `D` есть суперкласс `B` и примесь `C`. Классы могут иметь только один суперкласс, но много примесей (используя ключевыое слово `extends` и `with` соответственно). Примеси и суперкласс могут иметь один и тот же супертип. Теперь давайте рассмотрим более интересный пример, начиная с абстрактного класса: @@ -44,7 +44,7 @@ abstract class AbsIterator { ``` Класс имеет абстрактный тип `T` и методы стандартного итератора. -Далее создаем конкрентную реализацию класса (все абстрактные члены `T`, `hasNext`, и `next` должны быть реализованы): +Далее создаем конкретную реализацию класса (все абстрактные члены `T`, `hasNext`, и `next` должны быть реализованы): ```tut class StringIterator(s: String) extends AbsIterator { @@ -69,7 +69,7 @@ trait RichIterator extends AbsIterator { ``` У этого трейта реализован метод `foreach` который постоянно вызывает переданную ему функцию `f: T => Unit` на каждом новом элементе (`next()`) до тех пор пока в итераторе содержатся элементы (`while (hasNext)`). Поскольку `RichIterator` это трейт, ему не нужно реализовывать членов абстрактного класса `AbsIterator`. -Мы бы хотели объеденить функциональность `StringIterator` и `RichIterator` в один класс. +Мы бы хотели объединить функциональность `StringIterator` и `RichIterator` в один класс. ```tut object StringIteratorTest extends App { @@ -80,4 +80,4 @@ object StringIteratorTest extends App { ``` Новый класс `RichStringIter` включает `StringIterator` как суперкласс и `RichIterator` как примесь. -Используя только одиночное наследование мы бы не могли добится тогоже уровня гибкости. +Используя только одиночное наследование мы бы не могли добиться того же уровня гибкости. diff --git a/_ru/tour/multiple-parameter-lists.md b/_ru/tour/multiple-parameter-lists.md index 77ee1b64fb..d1938cb4b0 100644 --- a/_ru/tour/multiple-parameter-lists.md +++ b/_ru/tour/multiple-parameter-lists.md @@ -31,7 +31,7 @@ val res = numbers.foldLeft(0)((m, n) => m + n) print(res) // 55 ``` -Множественные списки параметров имеют избыточный синтаксис, поэтому их следует использоваться экономно. Можем предложить следующие варианты для использования множественных списков (карирования): +Множественные списки параметров имеют избыточный синтаксис, поэтому их следует использоваться экономно. Можем предложить следующие варианты для использования множественных списков (каррирования): #### Отдельный функциональный параметр Функцию `op` можно выделить в отдельный функциональный параметр у `foldLeft`, благодаря такому выделению становится возможен более элегантный стиль передачи анонимной функции в метод. Без такого выделения код выглядел бы следующим образом: @@ -39,12 +39,12 @@ print(res) // 55 numbers.foldLeft(0, {(m: Int, n: Int) => m + n}) ``` - Обратите внимание, что использование отдельного функционального параметра позволяет нам использовать автоматическое выведение типа для него, что делает код еще более кратким, это было бы невозможно без карирования. + Обратите внимание, что использование отдельного функционального параметра позволяет нам использовать автоматическое выведение типа для него, что делает код еще более кратким, это было бы невозможно без каррирования. ``` numbers.foldLeft(0)(_ + _) ``` - Если в утверждении `numbers.foldLeft(0)(_ + _)` зафиксировать отдельный параметр `z`, мы получим частично определенную функцию, которую можно переиспользовать, как показанно ниже: + Если в утверждении `numbers.foldLeft(0)(_ + _)` зафиксировать отдельный параметр `z`, мы получим частично определенную функцию, которую можно переиспользовать, как показано ниже: ```tut val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) val numberFunc = numbers.foldLeft(List[Int]())_ // z = Empty.List[Int] @@ -63,8 +63,8 @@ val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) numbers.foldLeft(0)((sum, item) => sum + item) // Общая Форма numbers.foldRight(0)((sum, item) => sum + item) // Общая Форма -numbers.foldLeft(0)(_+_) // Каррированная Форма -numbers.foldRight(0)(_+_) // Каррированная Форма +numbers.foldLeft(0)(_+_) // Форма с каррированием +numbers.foldRight(0)(_+_) // Форма с каррированием (0 /: numbers)(_+_) // Используется вместо foldLeft (numbers :\ 0)(_+_) // Используется вместо foldRight diff --git a/_ru/tour/named-arguments.md b/_ru/tour/named-arguments.md index 138ceb0492..defb9d4282 100644 --- a/_ru/tour/named-arguments.md +++ b/_ru/tour/named-arguments.md @@ -25,7 +25,7 @@ printName("John", "Smith") // Prints "John Smith" printName(first = "John", last = "Smith") // Prints "John Smith" printName(last = "Smith", first = "John") // Prints "John Smith" ``` -Обратите внимание, что при указании имени параметра, порядок аргумента может быть изменен. Однако если какие-то аргументы именованного, а другие нет, то аргументы без имени должны стоять на первом месте и располагаться в том порядке, в котором описанны параметры метода. +Обратите внимание, что при указании имени параметра, порядок аргумента может быть изменен. Однако если какие-то аргументы именованного, а другие нет, то аргументы без имени должны стоять на первом месте и располагаться в том порядке, в котором описаны параметры метода. ```tut:fail printName(last = "Smith", "john") // ошибка: позиция после именованного аргумента diff --git a/_ru/tour/nested-functions.md b/_ru/tour/nested-functions.md index 20698c4387..ef082c36e4 100644 --- a/_ru/tour/nested-functions.md +++ b/_ru/tour/nested-functions.md @@ -13,7 +13,7 @@ previous-page: higher-order-functions --- -В Scala возможно объявление метода вкладывать в тело другого метода. Это реализованно в следующем примере, в котором метод `factorial` используется для вычисления факториала заданного числа: +В Scala возможно объявление метода вкладывать в тело другого метода. Это реализовано в следующем примере, в котором метод `factorial` используется для вычисления факториала заданного числа: {% scalafiddle %} ```tut diff --git a/_ru/tour/operators.md b/_ru/tour/operators.md index 442fb42705..1609ce60f5 100644 --- a/_ru/tour/operators.md +++ b/_ru/tour/operators.md @@ -18,7 +18,7 @@ prerequisite-knowledge: case-classes 10.+(1) ``` -Однако легче воспринимать код когда такие методы записанны как инфиксный оператор: +Однако легче воспринимать код, когда такие методы записаны как инфиксный оператор: ``` 10 + 1 ``` diff --git a/_ru/tour/package-objects.md b/_ru/tour/package-objects.md index 5e51e4a274..35cab8b921 100644 --- a/_ru/tour/package-objects.md +++ b/_ru/tour/package-objects.md @@ -61,7 +61,7 @@ object PrintPlanted { } ``` -Объекты пакета такиеже как и другие объекты, это означает, что вы можете использовать наследование, при этом сразу нескольких трейтов: +Объекты пакета ведут себя также, как и любые другие объекты. Это означает, что вы можете использовать наследование, при этом сразу нескольких трейтов: ``` package object fruits extends FruitAliases with FruitHelpers { diff --git a/_ru/tour/pattern-matching.md b/_ru/tour/pattern-matching.md index 28466af962..50aa1e4638 100644 --- a/_ru/tour/pattern-matching.md +++ b/_ru/tour/pattern-matching.md @@ -60,7 +60,7 @@ case class VoiceRecording(contactName: String, link: String) extends Notificatio ``` `Notification` - абстрактный суперкласс, от которого наследуются три конкретных типа реализаций классов образцов `Email`, `SMS`, и `VoiceRecording`. Теперь мы можем делать сопоставление с примером используя в качестве примера один из этих классов образцов. -При сопоставлении с классом обазцом мы можем сразу извлекать параметры из которых состоит класс (благодаря автоматическому использованию [объекта распаковщика](extractor-objects.html)): +При сопоставлении с классом образцом мы можем сразу извлекать параметры из которых состоит класс (благодаря автоматическому использованию [извлекающего объекта](extractor-objects.html)): ``` def showNotification(notification: Notification): String = { @@ -83,7 +83,7 @@ println(showNotification(someVoiceRecording)) // выводит "you received a Функция `showNotification` принимает в качестве параметра абстрактный тип `Notification` который проверяет по образцам (т.е. выясняет, является ли он классом `Email`, `SMS` или `VoiceRecording`). В `case Email(email, title, _)`поля `email` и `title` используются в возвращаемом значении, а вот поле `body` игнорируется благодаря символу `_`. ## Ограждения примеров -Ограждения примеров - это просто логические выражения, которые используются для того, чтобы сделать выбор более специфичным. Просто добавьте `if <булевское выражение>` после примера. +Ограждения примеров - это просто логические выражения, которые используются для того, чтобы сделать выбор более специфичным. Просто добавьте `if <логическое выражение>` после примера. ``` def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = { diff --git a/_ru/tour/polymorphic-methods.md b/_ru/tour/polymorphic-methods.md index 5d696cb7bc..a430aaf654 100644 --- a/_ru/tour/polymorphic-methods.md +++ b/_ru/tour/polymorphic-methods.md @@ -33,4 +33,4 @@ println(listOfDuplicates("La", 8)) // List(La, La, La, La, La, La, La, La) В первом вызове метода мы явно указываем параметр типа, записывая `[Int]`. Поэтому первым аргументом должен быть `Int` и тип возвращаемого значения будет `List[Int]`. -Во втором вызове показанно, что вам не всегда нужно явно указывать параметр типа. Часто компилятор сам может вывести тип исходя из контекста или типа передаваемых аргументов. В этом варианте `"La"` - это `String`, поэтому компилятор знает, что `A` должен быть `String`. +Во втором вызове показано, что вам не всегда нужно явно указывать параметр типа. Часто компилятор сам может вывести тип исходя из контекста или типа передаваемых аргументов. В этом варианте `"La"` - это `String`, поэтому компилятор знает, что `A` должен быть `String`. diff --git a/_ru/tour/regular-expression-patterns.md b/_ru/tour/regular-expression-patterns.md index 8b3c138744..eeb8b39312 100644 --- a/_ru/tour/regular-expression-patterns.md +++ b/_ru/tour/regular-expression-patterns.md @@ -28,7 +28,7 @@ numberPattern.findFirstMatchIn("awesomepassword") match { В приведенном выше примере `numberPattern` - это `Regex` (регулярное выражение), которое мы используем, чтобы убедиться, что пароль содержит число. -Используя круглые скобки можно объеденять сразу несколько групп регулярных выражений. +Используя круглые скобки можно объединять сразу несколько групп регулярных выражений. ```tut import scala.util.matching.Regex diff --git a/_ru/tour/self-types.md b/_ru/tour/self-types.md index 10a6539ca1..34ea641049 100644 --- a/_ru/tour/self-types.md +++ b/_ru/tour/self-types.md @@ -18,7 +18,7 @@ prerequisite-knowledge: nested-classes, mixin-class-composition Самоописываемый тип - это способ сузить тип `this` или другого идентификатора, который ссылается на `this`. Синтаксис похож на синтаксис обычной функции, но означает кое-что иное. -Чтобы использовать самоописываемый тип в трейте, напишите идентификатор, тип другого трейта который хотите добавить и `=>` (например, `someIdentifier: SomeOtherTrait =>`). +Чтобы использовать самоописываемый тип в трейте напишите: идентификатор, тип другого трейта, который хотите добавить и `=>` (например, `someIdentifier: SomeOtherTrait =>`). ```tut trait User { def username: String diff --git a/_ru/tour/tour-of-scala.md b/_ru/tour/tour-of-scala.md index 4c982672ff..8074c70b1d 100644 --- a/_ru/tour/tour-of-scala.md +++ b/_ru/tour/tour-of-scala.md @@ -19,7 +19,7 @@ next-page: basics [на других ресурсах](/learn.html). ## Что такое Scala? -Scala - это современный мультипарадигмальный язык программирования, разработанный для выражения общих концепций программирования в простой, удобной и типобезопасной манере. Элегантно обьединяя особенности объектно-ориентированных и функциональных языков. +Scala - это современный мультипарадигмальный язык программирования, разработанный для выражения общих концепций программирования в простой, удобной и типобезопасной манере. Элегантно объединяя особенности объектно-ориентированных и функциональных языков. ## Scala объектно ориентированный ## Scala - это чистый объектно-ориентированный язык в том смысле, что [каждое значение - это объект](unified-types.html). Типы и поведение объектов описаны в [классах](classes.html) и [трейтах](traits.html)(признаках). Классы расширяются за счет подклассификации и гибкого [смешивания классов](mixin-class-composition.html), который используется как механизм замены множественного наследования. @@ -27,9 +27,9 @@ Scala - это чистый объектно-ориентированный яз ## Scala функциональный ## Scala также является функциональным языком в том смысле, что [каждая функция - это значение](unified-types.html). Scala предоставляет [легкий синтаксис](basics.html) для определения анонимных функций, поддерживает [функции высшего порядка](higher-order-functions.html), позволяет функциям быть [вложенными](nested-functions.html) и поддерживает [каррирование](multiple-parameter-lists.html). [Классы образцы](case-classes.html) Scala имеют встроенную поддержку [сопоставления с примером](pattern-matching.html) для создания алгебраических типов данных, которые используются в большинстве функциональных языках программирования. [Объекты одиночки (сингэлтоны)](singleton-objects.html) предоставляют удобный способ группировки функций, не входящих в класс. -Кроме того, понятие Scala о сопоставлении с примером естественно распространяется на [обработку XML-данных](https://github.com/scala/scala-xml/wiki/XML-Processing) с помощью [шаблонов последовательного проектирования](regular-expression-patterns.html), через расширение [объектов распаковщиков](extractor-objects.html). В этом отношении [for представление](for-comprehensions.html) очень удобно использовать для создания запросов. Такие особенности делают Scala идеальным решением для разработки приложений по типу веб-сервисов. +Кроме того, понятие Scala о сопоставлении с примером естественно распространяется на [обработку XML-данных](https://github.com/scala/scala-xml/wiki/XML-Processing) с помощью [шаблонов последовательного проектирования](regular-expression-patterns.html), через расширение функционала [извлекающих объектов](extractor-objects.html). В этом отношении [for представление](for-comprehensions.html) очень удобно использовать для создания запросов. Такие особенности делают Scala идеальным решением для разработки приложений по типу веб-сервисов. -## Scala статически типизированый ## +## Scala статически типизированный ## Scala оснащен выразительной системой типов, которая обеспечивает безопасное и гармоничное использование абстракций. В частности, система типов поддерживает: * [обобщенные классы](generic-classes.html) diff --git a/_ru/tour/traits.md b/_ru/tour/traits.md index 0d029af770..538f18422e 100644 --- a/_ru/tour/traits.md +++ b/_ru/tour/traits.md @@ -15,7 +15,7 @@ prerequisite-knowledge: expressions, classes, generics, objects, companion-objec --- -Трейты используются чтоб обмениваться между классами информацией о структуре и полях. Они похожи на интерфейсы из Java 8. Классы и объекты могут расширять трейты, но трейты не могут быть созданны и поэтому не имеют параметров. +Трейты используются чтоб обмениваться между классами информацией о структуре и полях. Они похожи на интерфейсы из Java 8. Классы и объекты могут расширять трейты, но трейты не могут быть созданы и поэтому не имеют параметров. ## Объявление трейта Минимальное объявление трейта - это просто ключевое слово `trait` и его имя: @@ -81,4 +81,4 @@ animals.append(dog) animals.append(cat) animals.foreach(pet => println(pet.name)) // выведет "Harry" и "Sally" ``` -У трейта `Pet` есть абстрактное поле `name`, которое реализованно в классах `Cat` and `Dog`. В последней строке мы вызываем `pet.name`, который должен быть реализован в любом подтипе унаследованным от трейта `Pet`. +У трейта `Pet` есть абстрактное поле `name`, которое реализовано в классах `Cat` and `Dog`. В последней строке мы вызываем `pet.name`, который должен быть реализован в любом подтипе унаследованным от трейта `Pet`. diff --git a/_ru/tour/tuples.md b/_ru/tour/tuples.md index 59e9206cff..09bca25443 100644 --- a/_ru/tour/tuples.md +++ b/_ru/tour/tuples.md @@ -15,7 +15,7 @@ topics: tuples --- В Scala, a кортеж это класс контейнер содержащий упорядоченный набор элементов различного типа. -Кортежи неизменеямы. +Кортежи неизменяемы. Кортежи могут пригодиться, когда нам нужно вернуть сразу несколько значений из функции. @@ -26,7 +26,7 @@ val ingredient = ("Sugar" , 25):Tuple2[String, Int] ``` Такая запись создает кортеж размерности 2, содержащий пару элементов String и Int. -Кортежи в Скале - представленны серией классов: Tuple2, Tuple3 и т.д., до Tuple22. +Кортежи в Скале - представлены серией классов: Tuple2, Tuple3 и т.д., до Tuple22. Таким образом, создавая кортеж с n элементами (n лежащими между 2 и 22), Скала просто создает один из соответствующих классов, который параметризован типом входящих в состав элементов. В нашем примере, составляющие тип Tuple2[String, Int]. @@ -54,7 +54,7 @@ println(name) // Sugar println(quantity) // 25 ``` -Разборка данных кортежа может быть использованна в [сопоставлении с примером](pattern-matching.html) +Разборка данных кортежа может быть использована в [сопоставлении с примером](pattern-matching.html) ```tut val planetDistanceFromSun = List(("Mercury", 57.9), ("Venus", 108.2), ("Earth", 149.6 ), ("Mars", 227.9), ("Jupiter", 778.3)) @@ -92,4 +92,4 @@ for ((a, b) <- numPairs) { Значение () типа Unit по свой сути совпадает со значением () типа Tuple0. Может быть только одно значение такого типа, так как в нём нет элементов. -Иногда бывает трудно выбирать между кортежами и классами образцами. Как правило, классы образцы являются предпочтительным выбором, если сам класс-контейнер содержащий элементы представляет собой значимый смысл. +Иногда бывает трудно выбирать между кортежами и классами образцами. Как правило, классы образцы являются предпочтительным выбором, если сам класс-контейнер содержащий элементы сам по себе имеет значимый смысл. diff --git a/_ru/tour/unified-types.md b/_ru/tour/unified-types.md index 222a84d492..3322b08ee8 100644 --- a/_ru/tour/unified-types.md +++ b/_ru/tour/unified-types.md @@ -24,7 +24,7 @@ prerequisite-knowledge: classes, basics `AnyVal` представляет числовые типы. Существует девять предварительно определенных числовых типов и они никогда не могут быть равны 'null': `Double`, `Float`, `Long`, `Int`, `Short`, `Byte`, `Char`, `Unit`, и `Boolean`. `Unit` - это числовой тип, который не содержит значимой информации (также обозначает пустое множество). Есть только один представитель типа `Unit`, который можно объявить вот так: `()`. Все функции должны возвращать что-то, поэтому иногда `Unit` полезный для возврата тип. -`AnyRef` представляет ссылочные типы. Все типы, не относящиеся к "типам значений", называются ссылочными типами. Каждый объявлялемый пользователем тип в Scala является подтипом `AnyRef`. Если Scala исходить из контекста среды исполнения Java, `AnyRef` соответствует `java.lang.Object`. +`AnyRef` представляет ссылочные типы. Все типы, не относящиеся к "типам значений", называются ссылочными типами. Каждый объявляемый пользователем тип в Scala является подтипом `AnyRef`. Если Scala исходить из контекста среды исполнения Java, `AnyRef` соответствует `java.lang.Object`. Вот пример, демонстрирующий, что строки, целые числа, символы, логические значения и функции являются объектами, как и любой другой объект: @@ -77,6 +77,6 @@ val z: Long = y // Не подходит Вы также можете приводить к своему подтипу. Об этом мы поговорим позже в ходе нашего обзора. ## Nothing и Null -`Nothing` является подтипом всех типов, также называемым нижним типом. Нет значения, которое имеет тип `Nothing`. Обычно он используется чтоб дать сигнал о невычислимости, например брошено исключение, выход из программы, бесконечное зацикливание (т.е. это тип выражения, которое не вычисляется). +`Nothing` является подтипом всех типов, также называемым нижним типом. Нет значения, которое имеет тип `Nothing`. Обычно он используется чтоб дать сигнал о не вычислимости, например брошено исключение, выход из программы, бесконечное зацикливание (т.е. это тип выражения, которое не вычисляется). `Null` подтип всех ссылочных типов (т.е. любой подтип AnyRef). Он имеет одно значение, определяемое ключевым словом литерала `null`. `Null` предоставляется в основном для функциональной совместимости с другими языками JVM и почти никогда не должен использоваться в коде Scala. Об альтернативах `null` мы поговорим позднее. diff --git a/_ru/tour/variances.md b/_ru/tour/variances.md index 8e6a774522..db3d8d7a89 100644 --- a/_ru/tour/variances.md +++ b/_ru/tour/variances.md @@ -13,7 +13,7 @@ previous-page: generic-classes --- -Вариантность - это указание определенной специфики взаимосвязи между сложними связанными типам. Скала поддерживает вариантную аннотацую типов у [обобщенных классов](generic-classes.html), что позволяет им быть ковариантными, контрвариантными или инвариантными (если нет никакого указание на вариантность). Использование вариантности в системе типов позволяет устанавливать понятные взаимосвязи между сложными типами, в то время как отсутствие вариантности может ограничить повторное использование абстракции класса. +Вариантность - это указание определенной специфики взаимосвязи между сложными связанными типам. Scala поддерживает вариантную аннотацию типов у [обобщенных классов](generic-classes.html), что позволяет им быть ковариантными, контрвариантными или инвариантными (если нет никакого указание на вариантность). Использование вариантности в системе типов позволяет устанавливать понятные взаимосвязи между сложными типами, в то время как отсутствие вариантности может ограничить повторное использование абстракции класса. ```tut class Foo[+A] // ковариантный класс @@ -86,7 +86,7 @@ class CatPrinter extends Printer[Cat] { } ``` -Если `Printer[Cat]` знает, как распечатать любой класс `Cat` в консоли, а `Printer[Animal]` знает, как напечатать любое `Animal` в консоли, то разумно если `Printer[Animal]` также знает, как напечатать любое `Cat`. Обратного отношения нет, потому что `Printer[Cat]` не знает, как распечатать любой `Animal` на консоли. Чтоб иметь возможность заменить `Printer[Animal]` на `Printer[Cat]`, необоходимо `Printer[A]` сделать контрвариантным. +Если `Printer[Cat]` знает, как распечатать любой класс `Cat` в консоли, а `Printer[Animal]` знает, как напечатать любое `Animal` в консоли, то разумно если `Printer[Animal]` также знает, как напечатать любое `Cat`. Обратного отношения нет, потому что `Printer[Cat]` не знает, как распечатать любой `Animal` на консоли. Чтоб иметь возможность заменить `Printer[Animal]` на `Printer[Cat]`, необходимо `Printer[A]` сделать контрвариантным. ```tut object ContravarianceTest extends App { @@ -125,13 +125,13 @@ class Container[A](value: A) { } ``` -Может показаться что `Container[Cat]` должен также являтся и `Container[Animal]`, но позволить мутабельному обобщеному классу быть ковариантным было бы небезопасно. В данном примере очень важно, чтобы `Container` был инвариантным. Предположим, что `Container` на самом деле был ковариантным, что-то вроде этого могло случиться: +Может показаться что `Container[Cat]` должен также являться и `Container[Animal]`, но позволить мутабельному обобщенному классу быть ковариантным было бы небезопасно. В данном примере очень важно, чтобы `Container` был инвариантным. Предположим, что `Container` на самом деле был ковариантным, что-то вроде этого могло случиться: ``` val catContainer: Container[Cat] = new Container(Cat("Felix")) val animalContainer: Container[Animal] = catContainer animalContainer.setValue(Dog("Spot")) -val cat: Cat = catContainer.getValue // Ой, мы бы закончили собакой присвоеной к коту. +val cat: Cat = catContainer.getValue // Ой, мы бы закончили собакой присвоенной к коту. ``` К счастью, компилятор остановит нас прежде, чем мы зайдем так далеко. @@ -140,7 +140,7 @@ val cat: Cat = catContainer.getValue // Ой, мы бы закончили со Другим примером, который может помочь понять вариантность, является трейт `Function1[-T, +R]` из стандартной библиотеки Scala. `Function1` представляет собой функцию с одним параметром, где первый тип `T` представляет собой параметр типа, а второй тип `R` представляет собой тип результата. Функция `Function1` является контрвариантной в рамках типа принимаемого аргумента, а ковариантной - в рамках возвращаемого типа. Для этого примера мы будем использовать явное обозначение типа `A =>B` чтоб продемонстрировать `Function1[A, B]`. -Рассмотрим схожий пример `Cat`, `Dog`, `Animal` в тойже взаимосвязи что и раньше, плюс следующее: +Рассмотрим схожий пример `Cat`, `Dog`, `Animal` в той же взаимосвязи что и раньше, плюс следующее: ```tut abstract class SmallAnimal extends Animal From 036132c3e11e24330526384afbb49958d3256d37 Mon Sep 17 00:00:00 2001 From: "Kotobotov.ru" Date: Mon, 28 Jan 2019 06:01:44 +0700 Subject: [PATCH 3/5] :art: Improving style Took 2 hours 46 minutes --- _ru/tour/abstract-type-members.md | 16 ++++++++-------- _ru/tour/annotations.md | 8 ++++---- _ru/tour/basics.md | 18 +++++++++--------- _ru/tour/lower-type-bounds.md | 6 +++--- _ru/tour/tour-of-scala.md | 26 +++++++++++++------------- _ru/tour/unified-types.md | 4 ++-- _ru/tour/upper-type-bounds.md | 4 ++-- _ru/tour/variances.md | 14 +++++++------- 8 files changed, 48 insertions(+), 48 deletions(-) diff --git a/_ru/tour/abstract-type-members.md b/_ru/tour/abstract-type-members.md index 1fd14d1e3c..cb2a2ecc1e 100644 --- a/_ru/tour/abstract-type-members.md +++ b/_ru/tour/abstract-type-members.md @@ -14,8 +14,8 @@ prerequisite-knowledge: variance, upper-type-bound --- -Абстрактные типы, такие как трейты и абстрактные классы, которые могут содержать членов абстрактного типа. -Это означает, что только конкретный экземпляр определяет, каким именно будет этот тип. +Абстрактные типы, такие как трейты и абстрактные классы, могут содержать членов абстрактного типа. +Абстрактный означает, что только конкретный экземпляр определяет, каким именно будет тип. Вот пример: ```tut @@ -24,7 +24,7 @@ trait Buffer { val element: T } ``` -Здесь мы определили абстрактный вариант тип `T`, который используется для описания типа `element`. Мы можем расширить его в абстрактном классе, добавив верхнюю границу связанного с `T` типа, чтобы сделать его более конкретным. +Здесь мы определили абстрактный тип `T`, который используется для описания типа члена `element`. Мы можем расширить его в абстрактном классе, добавив верхнюю границу нового типа `U` связанного с `T`, делая описание типа более конкретным. ```tut abstract class SeqBuffer extends Buffer { @@ -33,9 +33,9 @@ abstract class SeqBuffer extends Buffer { def length = element.length } ``` -Обратите внимание, как мы можем использовать еще один абстрактный тип `type U` в качестве верхней границы типа. Класс `SeqBuffer` позволяет хранить в буфере только последовательности, указывая, что тип `T` должен быть подтипом `Seq[U]` для нового абстрактного типа `U`. +Обратите внимание, как мы можем использовать новый абстрактный тип `U` в качестве верхней границы типа. Класс `SeqBuffer` позволяет хранить в буфере только последовательности, указывая, что тип `T` должен быть подтипом `Seq[U]` для нового абстрактного типа `U`. -[Трейты](traits.html) или [классы](classes.html) с абстрактными членами типа часто используются в сочетании с анонимными экземплярами классов. Чтобы проиллюстрировать это рассмотрим программу, которая имеет дело с буфером, который ссылается на список целых чисел: +[Трейты](traits.html) или [классы](classes.html) с членами абстрактного типа часто используются в сочетании с анонимными экземплярами классов. Чтобы проиллюстрировать это рассмотрим программу, которая имеет дело с буфером, который ссылается на список целых чисел: ```tut abstract class IntSeqBuffer extends SeqBuffer { @@ -52,9 +52,9 @@ val buf = newIntSeqBuf(7, 8) println("length = " + buf.length) println("content = " + buf.element) ``` -Здесь класс `newIntSeqBuf` является создателем экземпляров `IntSeqBuffer`, использует анонимную реализацию класса `IntSeqBuffer` (т.е. `new IntSeqBuffer`), устанавливая тип `T` как `List[Int]`. +Здесь класс `newIntSeqBuf` создает экземпляры `IntSeqBuffer`, используя анонимную реализацию класса `IntSeqBuffer` (т.е. `new IntSeqBuffer`), устанавливая тип `T` как `List[Int]`. -Мы можем выводить тип класса из типа его членов и наоборот наоборот. Приведем версию кода, в которой выводится тип класса из типа его члена: +Мы можем вывести тип класса из типа его членов и наоборот. Приведем версию кода, в которой выводится тип класса из типа его члена: ```tut abstract class Buffer[+T] { @@ -74,4 +74,4 @@ println("length = " + buf.length) println("content = " + buf.element) ``` -Обратите внимание, что здесь нам необходимо использовать [вариантность в описании типа](variances.html) (`+T <: Seq[U]`) для того, чтобы скрыть конкретный тип реализации последовательности объектов, возвращаемых из метода `newIntSeqBuf`. \ No newline at end of file +Обратите внимание, что здесь необходимо использовать [вариантность в описании типа](variances.html) (`+T <: Seq[U]`) для того, чтобы скрыть конкретный тип реализации списка, возвращаемого из метода `newIntSeqBuf`. \ No newline at end of file diff --git a/_ru/tour/annotations.md b/_ru/tour/annotations.md index d15436162c..2016cd1603 100644 --- a/_ru/tour/annotations.md +++ b/_ru/tour/annotations.md @@ -57,14 +57,14 @@ def factorial(x: Int): Int = { ## Аннотации, влияющие на генерацию кода -Некоторые аннотации типа `@inline` влияют на сгенерированный код (т.е. ваш jar-файл может иметь другой код, чем если бы вы не использовали аннотацию). Такая аннотация означает вставку всего кода в тело метода вместо вызова. Полученный байт-код длиннее, но, надеюсь, работает быстрее. Использование аннотации `@inline` не гарантирует, что метод будет встроен, но заставит компилятор сделать это, если и только если будут соблюдены некоторые практические требования к размеру сгенерированного кода. +Некоторые аннотации типа `@inline` влияют на сгенерированный код (т.е. в результате сам код вашего jar-файл может отличаться). Такая аннотация означает вставку всего кода в тело метода вместо вызова. Полученный байт-код длиннее, но, надеюсь, работает быстрее. Использование аннотации `@inline` не гарантирует, что метод будет встроен, но заставит компилятор сделать это, если и только если будут соблюдены некоторые разумные требования к размеру сгенерированного кода. ### Java аннотации ### Есть некоторые отличий синтаксиса аннотаций, если пишется Scala код, который взаимодействует с Java. **Примечание:**Убедитесь, что вы используете опцию `-target:jvm-1.8` с аннотациями Java. -Java имеет определяемые пользователем метаданные в виде [аннотаций](https://docs.oracle.com/javase/tutorial/java/annotations/). Ключевой особенностью аннотаций является то, что они полагаются на указание пар ключ-значение для инициализации своих элементов. Например, если нам нужна аннотация для отслеживания источника какого-то класса, мы можем определить её как +Java имеет определяемые пользователем метаданные в виде [аннотаций](https://docs.oracle.com/javase/tutorial/java/annotations/). Ключевой особенностью аннотаций является то, что они задаются в виде пар ключ-значение для инициализации своих элементов. Например, если нам нужна аннотация для отслеживания источника какого-то класса, мы можем определить её как ``` @interface Source { @@ -98,7 +98,7 @@ class MyScalaClass ... } ``` -А затем использовать следующим образом +А затем можно использовать следующим образом ``` @SourceURL("http://coders.com/") @@ -112,7 +112,7 @@ public class MyClass extends HisClass ... class MyScalaClass ... ``` -Элемент `mail` был указан со значением по умолчанию, поэтому нам не нужно явно указывать его значение. Однако, если нам нужно это сделать, мы не можем смешивать эти два стиля в Java: +Элемент `mail` был указан со значением по умолчанию, поэтому нам не нужно явно указывать его значение. Мы не можем смешивать эти два стиля в Java: ``` @SourceURL(value = "http://coders.com/", diff --git a/_ru/tour/basics.md b/_ru/tour/basics.md index 78550faec5..bf0257a403 100644 --- a/_ru/tour/basics.md +++ b/_ru/tour/basics.md @@ -26,7 +26,7 @@ previous-page: tour-of-scala Это простой способ поэкспериментировать со Scala кодом без всяких настроек. Большинство примеров кода в этой документации также интегрированы с ScalaFiddle, -поэтому вы можете непосредственно поэкспериментировать с ними, просто нажав кнопку Run. +поэтому вы можете поэкспериментировать с ними, просто нажав кнопку Run. ## Выражения @@ -47,7 +47,7 @@ println("Hello," + " world!") // Hello, world! ### Значения -Результаты выражений можно назвать с помощью ключевого слова `val`. +Результаты выражений можно присваивать именам с помощью ключевого слова `val`. ```tut val x = 1 + 1 @@ -63,7 +63,7 @@ println(x) // 2 x = 3 // Не компилируется. ``` -Типы значений могут быть выведены автоматически, но вы также можете явно указать тип, как показано ниже: +Типы значений могут быть выведены автоматически, но можно и явно указать тип, как показано ниже: ```tut val x: Int = 1 + 1 @@ -73,7 +73,7 @@ val x: Int = 1 + 1 ### Переменные -Переменные похожи на значения константы, за исключением того, что их можно переназначить заново. Вы можете объявить переменную с помощью ключевого слова `var`. +Переменные похожи на значения константы, за исключением того, что их можно присваивать заново. Вы можете объявить переменную с помощью ключевого слова `var`. ```tut var x = 1 + 1 @@ -81,7 +81,7 @@ x = 3 // Компилируется потому что "x" объявлен с println(x * x) // 9 ``` -Как и в случае со значениями, вы можете явно указать тип, если хотите: +Как и в случае со значениями, вы можете явно указать тип, если захотите: ```tut var x: Int = 1 + 1 @@ -190,7 +190,7 @@ class Greeter(prefix: String, suffix: String) { println(prefix + name + suffix) } ``` -Возвращаемый тип метода `greet` это `Unit`, используется тогда, когда не имеет смысла что-либо возвращать. Аналогично `void` в Java и C. (Поскольку каждое выражение Scala должно иметь какое-то значение, поэтому, при отсутствии возвращающегося значения, возвращается одиночка (сингэлтон) типа Unit. Явным образом его можно задать как `()`, оно не несет какой-либо информации.) +Возвращаемый тип метода `greet` это `Unit`, используется тогда, когда не имеет смысла что-либо возвращать. Аналогично `void` в Java и C. (Поскольку каждое выражение Scala должно иметь какое-то значение, то при отсутствии возвращающегося значения, возвращается экземпляр типа Unit. Явным образом его можно задать как `()`, он не несет какой-либо информации.) Вы можете создать экземпляр класса используя ключевое слово `new`. @@ -233,11 +233,11 @@ if (point == yetAnotherPoint) { } // Point(1,2) и Point(2,2) разные. ``` -Есть еще куча вещей которые мы бы хотели рассказать про классы образцы, мы уверены, что вы влюбитесь в них! Обязательно рассмотрим их подробнее немного [позже](case-classes.html). +Есть еще много деталей, которые мы бы хотели рассказать про классы образцы, мы уверены, что вы влюбитесь в них! Обязательно рассмотрим их [позже](case-classes.html). ## Объекты -Объекты задаются и существуют в единственным экземпляре. Вы можете думать о них как об одиночках своего собственного класса. +Объекты задаются и существуют в единственным экземпляре. Вы можете думать о них как об одиночках (сингэлтонах) своего собственного класса. Вы можете задать объекты при помощи ключевого слова `object`. @@ -260,7 +260,7 @@ val newerId: Int = IdFactory.create() println(newerId) // 2 ``` -Позже рассмотрим объекты более [подробно](singleton-objects.html). +Позже мы рассмотрим объекты [подробнее](singleton-objects.html). ## Трейты diff --git a/_ru/tour/lower-type-bounds.md b/_ru/tour/lower-type-bounds.md index fcab55a561..fe42c61630 100644 --- a/_ru/tour/lower-type-bounds.md +++ b/_ru/tour/lower-type-bounds.md @@ -1,6 +1,6 @@ --- layout: tour -title: Нижнее Ограничение Типов +title: Нижнее Ограничение Типа discourse: true @@ -14,7 +14,7 @@ prerequisite-knowledge: upper-type-bounds, generics, variance --- -В то время как [верхнее ограничение типов](upper-type-bounds.html) ограничивают тип до подтипа стороннего типа, *нижнее ограничение типов* объявляют тип супертипом стороннего типа. Термин `B >: A` выражает, что параметр типа `B` или абстрактный тип `B` относится к супертипу типа `A`. В большинстве случаев `A` будет задавать тип класса, а `B` задавать тип метода. +В то время как [верхнее ограничение типа](upper-type-bounds.html) ограничивает тип до подтипа стороннего типа, *нижнее ограничение типа* объявляют тип супертипом стороннего типа. Термин `B >: A` выражает, то что параметр типа `B` или абстрактный тип `B` относится к супертипу типа `A`. В большинстве случаев `A` будет задавать тип класса, а `B` задавать тип метода. Вот пример, где это полезно: @@ -36,7 +36,7 @@ case class Nil[+B]() extends Node[B] { В данной программе реализован связанный список. `Nil` представляет пустой список. Класс `ListNode` - это узел, который содержит элемент типа `B` (`head`) и ссылку на остальную часть списка (`tail`). Класс `Node` и его подтипы ковариантны, потому что у нас указанно `+B`. -Однако эта программа _не компилируется_, потому что параметр `elem` в `prepend` имеет тип `B`, который мы объявили *ко*вариантным. Так это не работает, потому что функции *контр*вариантны в типах своих параметров и *ко*вариантны типах в своих результатов. +Однако эта программа _не компилируется_, потому что параметр `elem` в `prepend` имеет тип `B`, который мы объявили *ко*вариантным. Так это не работает, потому что функции *контр*вариантны в типах своих параметров и *ко*вариантны в типах своих результатов. Чтобы исправить это, необходимо перевернуть вариантность типа параметра `elem` в `prepend`. Для этого мы вводим новый тип для параметра `U`, у которого тип `B` указан в качестве нижней границы типа. diff --git a/_ru/tour/tour-of-scala.md b/_ru/tour/tour-of-scala.md index 8074c70b1d..7fcd19a9c9 100644 --- a/_ru/tour/tour-of-scala.md +++ b/_ru/tour/tour-of-scala.md @@ -15,19 +15,19 @@ next-page: basics Здесь вы увидите вводное описание наиболее часто используемых возможностей Scala. Этот обзор предназначен для новичков в изучении языка. -Это всего лишь небольшая экскурсия, а не полный курс освоения языка. Для глубокого погружения рекомендуем почитать [книги](/books.html) или воспользоваться курсами +Это лишь небольшая экскурсия, а не полный курс освоения языка. Для глубокого погружения рекомендуем почитать [книги](/books.html) или воспользоваться курсами [на других ресурсах](/learn.html). ## Что такое Scala? Scala - это современный мультипарадигмальный язык программирования, разработанный для выражения общих концепций программирования в простой, удобной и типобезопасной манере. Элегантно объединяя особенности объектно-ориентированных и функциональных языков. ## Scala объектно ориентированный ## -Scala - это чистый объектно-ориентированный язык в том смысле, что [каждое значение - это объект](unified-types.html). Типы и поведение объектов описаны в [классах](classes.html) и [трейтах](traits.html)(признаках). Классы расширяются за счет подклассификации и гибкого [смешивания классов](mixin-class-composition.html), который используется как механизм замены множественного наследования. +Scala - это чистый объектно-ориентированный язык в том смысле, что [каждое значение - это объект](unified-types.html). Типы и поведение объектов описаны в [классах](classes.html) и [трейтах](traits.html)(характеристиках объектов). Классы расширяются за счет механизма наследования и гибкого [смешивания классов](mixin-class-composition.html), который используется для замены множественного наследования. ## Scala функциональный ## -Scala также является функциональным языком в том смысле, что [каждая функция - это значение](unified-types.html). Scala предоставляет [легкий синтаксис](basics.html) для определения анонимных функций, поддерживает [функции высшего порядка](higher-order-functions.html), позволяет функциям быть [вложенными](nested-functions.html) и поддерживает [каррирование](multiple-parameter-lists.html). [Классы образцы](case-classes.html) Scala имеют встроенную поддержку [сопоставления с примером](pattern-matching.html) для создания алгебраических типов данных, которые используются в большинстве функциональных языках программирования. [Объекты одиночки (сингэлтоны)](singleton-objects.html) предоставляют удобный способ группировки функций, не входящих в класс. +Scala также является функциональным языком в том смысле, что [каждая функция - это значение](unified-types.html). Scala предоставляет [легкий синтаксис](basics.html) для определения анонимных функций, поддерживает [функции высшего порядка](higher-order-functions.html), поддерживает [вложенные функции](nested-functions.html), а также [каррирование](multiple-parameter-lists.html). Scala имеют встроенную поддержку алгебраических типов данных, которые используются в большинстве функциональных языках программирования (эта поддержка базируется на механизме [сопоставления с примером](pattern-matching.html), где в качестве примера выступают [классы образцы](case-classes.html) ). [Объекты](singleton-objects.html) предоставляют удобный способ группировки функций, не входящих в класс. -Кроме того, понятие Scala о сопоставлении с примером естественно распространяется на [обработку XML-данных](https://github.com/scala/scala-xml/wiki/XML-Processing) с помощью [шаблонов последовательного проектирования](regular-expression-patterns.html), через расширение функционала [извлекающих объектов](extractor-objects.html). В этом отношении [for представление](for-comprehensions.html) очень удобно использовать для создания запросов. Такие особенности делают Scala идеальным решением для разработки приложений по типу веб-сервисов. +Вдобавок к этому, концепция сопоставления с примером логично переносится на [обработку XML-данных](https://github.com/scala/scala-xml/wiki/XML-Processing) используя в качестве примера [регулярные выражения](regular-expression-patterns.html), при поддержке функционала [извлекающих объектов](extractor-objects.html). Для еще большего удобства обработки данных представлена схема формирования запросов с использованием [for-выражения](for-comprehensions.html). Такие возможности делают Scala идеальным решением для разработки приложений по типу веб-сервисов. ## Scala статически типизированный ## Scala оснащен выразительной системой типов, которая обеспечивает безопасное и гармоничное использование абстракций. В частности, система типов поддерживает: @@ -41,24 +41,24 @@ Scala оснащен выразительной системой типов, к * [неявные параметры](implicit-parameters.html) и [неявные преобразования](implicit-conversions.html) * [полиморфные методы](polymorphic-methods.html) -[Выведение типов](type-inference.html) означает, что пользователю не обязательно описывать код избыточной информацией о типах. -Такой функционал обеспечивает мощную основу для безопасного переиспользования программных абстракций и типобезопасного расширения программного обеспечения. +[Выведение типов](type-inference.html) означает, что разработчику не обязательно добавлять в код избыточную информацию о типах. +Такой функционал обеспечивает основу для безопасного переиспользования абстракций и типобезопасного развития программного обеспечения. ## Scala расширяемый ## -Обычно разработка специфичных для предметной области приложений требует специфичных для этой предметной области языковых возможностей. Scala предоставляет уникальную комбинацию механизмов, которые позволяют легко добавлять новые языковые конструкции в виде библиотек. +Зачастую разработка приложений для очень специфичных областей требует специфичных для этих областей языковых возможностей, либо отдельных специализированных языков программирования. Вместо этого Scala предлагает уникальные механизмы, для легкой модификации и расширения самого языка. -Во многих случаях это можно сделать без использования средств мета-программирования, таких как макросы. Например: +Во многих случаях такое можно сделать без использования средств мета-программирования, таких как макросы. Например: -* [Неявные классы](http://docs.scala-lang.org/overviews/core/implicit-classes.html) позволяет добавлять новые методы к уже существующим типам. -* [Интерполяция строк](/overviews/core/string-interpolation.html) расширяется пользователем с помощью пользовательских интерполяторов. +* [Неявные классы](http://docs.scala-lang.org/overviews/core/implicit-classes.html) позволяют добавлять новые методы к уже существующим. +* [Интерполяция строк](/overviews/core/string-interpolation.html) позволяет добавлять обработку строк (расширяется разработчиком с помощью интерполяторов). ## Scala совместимый -Scala полностью совместим с популярной средой Java Runtime Environment (JRE). Взаимодействие с основным объектно-ориентированным языком программирования Java происходит максимально гладко. Более новые функции Java, такие как SAM, [лямбды](higher-order-functions.html), [аннотации](annotations.html) и [дженерики](generic-classes.html), имеют прямые аналоги в Scala. +Scala полностью совместим с популярной средой Java Runtime Environment (JRE). Взаимодействие с основным объектно-ориентированным языком программирования Java происходит максимально гладко. Новые функции Java, такие как SAM, [лямбды](higher-order-functions.html), [аннотации](annotations.html) и [дженерики](generic-classes.html), имеют прямые аналоги в Scala. -Те функции Scala, которые не имеют аналогов в Java, такие как [параметры по умолчанию](default-parameter-values.html) и [именованные параметры](named-arguments.html), компилируются как можно ближе к Java. Scala имеет такую же компиляционную модель (отдельная компиляция, динамическая загрузка классов), как у Java и позволяет получить доступ к тысячам уже существующих высококачественных библиотек. +Те функции Scala, которые не имеют аналогов в Java, такие как [параметры по умолчанию](default-parameter-values.html) и [именованные параметры](named-arguments.html), компилируются как можно ближе к Java. Scala имеет такую же компиляционную модель (отдельная компиляция, динамическая загрузка классов), как у Java, что позволяет получить доступ к тысячам уже существующих высококачественных библиотек. ## Наслаждайтесь туром! -Предлагаю перейти на [следующую страницу](basics.html) нашего тура, дабы продолжить знакомство. \ No newline at end of file +Для продолжения знакомства предлагаю перейти на [следующую страницу](basics.html) нашего тура. \ No newline at end of file diff --git a/_ru/tour/unified-types.md b/_ru/tour/unified-types.md index 3322b08ee8..521ff51754 100644 --- a/_ru/tour/unified-types.md +++ b/_ru/tour/unified-types.md @@ -24,7 +24,7 @@ prerequisite-knowledge: classes, basics `AnyVal` представляет числовые типы. Существует девять предварительно определенных числовых типов и они никогда не могут быть равны 'null': `Double`, `Float`, `Long`, `Int`, `Short`, `Byte`, `Char`, `Unit`, и `Boolean`. `Unit` - это числовой тип, который не содержит значимой информации (также обозначает пустое множество). Есть только один представитель типа `Unit`, который можно объявить вот так: `()`. Все функции должны возвращать что-то, поэтому иногда `Unit` полезный для возврата тип. -`AnyRef` представляет ссылочные типы. Все типы, не относящиеся к "типам значений", называются ссылочными типами. Каждый объявляемый пользователем тип в Scala является подтипом `AnyRef`. Если Scala исходить из контекста среды исполнения Java, `AnyRef` соответствует `java.lang.Object`. +`AnyRef` представляет ссылочные типы. Все типы, не относящиеся к "числовым типам", называются ссылочными типами. Каждый объявляемый пользователем тип в Scala является подтипом `AnyRef`. Если в Scala исходить из контекста среды исполнения Java, `AnyRef` соответствует `java.lang.Object`. Вот пример, демонстрирующий, что строки, целые числа, символы, логические значения и функции являются объектами, как и любой другой объект: @@ -71,7 +71,7 @@ val number: Int = face // 9786 ``` val x: Long = 987654321 val y: Float = x // 9.8765434E8 -val z: Long = y // Не подходит +val z: Long = y // обратно не подходит ``` Вы также можете приводить к своему подтипу. Об этом мы поговорим позже в ходе нашего обзора. diff --git a/_ru/tour/upper-type-bounds.md b/_ru/tour/upper-type-bounds.md index c4e6717c8a..7446261c1a 100644 --- a/_ru/tour/upper-type-bounds.md +++ b/_ru/tour/upper-type-bounds.md @@ -13,8 +13,8 @@ previous-page: variances --- -В Скале [параметры типа](generic-classes.html) и [члены абстрактного типа](abstract-type-members.html) могут быть ограничены определенными диапазонами. Такие диапазоны ограничивают конкретные значение типа и, возможно, предоставляют больше информации о членах таких типов. _Верхнее ограничение типа_ `T <: A` указывает на то что тип `T` относится к подтипу типа `A`. -Приведем пример, демонстрирующий верхнее ограничение типа для типа класса `PetContainer`: +В Scala [параметры типа](generic-classes.html) и [члены абстрактного типа](abstract-type-members.html) могут быть ограничены определенными диапазонами. Такие диапазоны ограничивают конкретные значение типа и, возможно, предоставляют больше информации о членах таких типов. _Верхнее ограничение типа_ `T <: A` указывает на то что тип `T` относится к подтипу типа `A`. +Приведем пример, демонстрирующий верхнее ограничение для типа класса `PetContainer`: ```tut abstract class Animal { diff --git a/_ru/tour/variances.md b/_ru/tour/variances.md index db3d8d7a89..15f4604457 100644 --- a/_ru/tour/variances.md +++ b/_ru/tour/variances.md @@ -23,7 +23,7 @@ class Baz[A] // инвариантными класс ### Ковариантность -Параметр типа `A` обобщенного класса можно сделать ковариантным с помощью аннотации `+A`. Для некоторого класса `List[+A]`, указание `A` в виде коварианта подразумевает, что для двух типов `A` и `B`, где `A` является подтипом `B`, `List[A]` представляет собой подтип `List[B]`. Что позволяет нам создавать очень полезные и интуитивно понятные взаимоотношения между типами с использованием дженериков. +Параметр типа `A` обобщенного класса можно сделать ковариантным с помощью аннотации `+A`. Для некоторого класса `List[+A]`, указание `A` в виде коварианта подразумевает, что для двух типов `A` и `B`, где `A` является подтипом `B`, `List[A]` представляет собой подтип `List[B]`. Что позволяет нам создавать очень полезные и интуитивно понятные взаимоотношения между типами с использованием обобщений (дженериков). Рассмотрим простую структуру классов: @@ -35,7 +35,7 @@ case class Cat(name: String) extends Animal case class Dog(name: String) extends Animal ``` -И `Cat` и `Dog` являются подтипами `Animal`. Стандартная библиотека Scala имеет обобщенный неизменяемый тип `List[+A]`, где параметр типа `A` является ковариантным. Это означает, что `List[Cat]` - это `List[Animal]`, а `List[Dog]` - это также `List[Animal]`. Интуитивно понятно, что список кошек и список собак - это список животных, и вы должны быть в состоянии заменить любого из них на `List[Animal]`. +И `Cat` (кошка) и `Dog`(собака) являются подтипами `Animal`(животное). Стандартная библиотека Scala имеет обобщенный неизменяемый тип `List[+A]`, где параметр типа `A` является ковариантным. Это означает, что `List[Cat]` - это `List[Animal]`, а `List[Dog]` - это также `List[Animal]`. Интуитивно понятно, что список кошек и список собак - это список животных и вы должны быть в состоянии заменить любого из них на `List[Animal]`. В следующем примере метод `printAnimalNames` принимает в качестве аргумента список животных и выводит их имена в новой строке. Если бы `List[A]` не был ковариантным, последние два вызова метода не компилировались бы, что сильно ограничило бы полезность метода `printAnimalNames`. @@ -62,7 +62,7 @@ object CovarianceTest extends App { ### Контрвариантность -Параметр типа `A` обобщенного класса можно сделать контрвариантным с помощью аннотации `-A`. Это создает схожее, но противоположное ковариационному, взаимоотношения между типом параметра и подтипами класса. То есть, для некого класса `Writer[-A]`, указание `A` контрвариантным подразумевает, что для двух типов `A` и `B` где `A` является подтипом `B`, `Writer[B]` является подтипом `Writer[A]`. +Параметр типа `A` обобщенного класса можно сделать контрвариантным с помощью аннотации `-A`. Это создает схожее, но противоположное ковариантным, взаимоотношения между типом параметра и подтипами класса. То есть, для некого класса `Writer[-A]`, указание `A` контрвариантным подразумевает, что для двух типов `A` и `B` где `A` является подтипом `B`, `Writer[B]` является подтипом `Writer[A]`. Рассмотрим классы `Cat`, `Dog`, и `Animal`, описанные выше для следующего примера: @@ -86,7 +86,7 @@ class CatPrinter extends Printer[Cat] { } ``` -Если `Printer[Cat]` знает, как распечатать любой класс `Cat` в консоли, а `Printer[Animal]` знает, как напечатать любое `Animal` в консоли, то разумно если `Printer[Animal]` также знает, как напечатать любое `Cat`. Обратного отношения нет, потому что `Printer[Cat]` не знает, как распечатать любой `Animal` на консоли. Чтоб иметь возможность заменить `Printer[Animal]` на `Printer[Cat]`, необходимо `Printer[A]` сделать контрвариантным. +Если `Printer[Cat]` знает, как распечатать любой класс `Cat` в консоли, а `Printer[Animal]` знает, как распечатать любое `Animal` в консоли, то разумно если `Printer[Animal]` также знает, как распечатать любое `Cat`. Обратного отношения нет, потому что `Printer[Cat]` не знает, как распечатать любой `Animal` в консоли. Чтоб иметь возможность заменить `Printer[Animal]` на `Printer[Cat]`, необходимо `Printer[A]` сделать контрвариантным. ```tut object ContravarianceTest extends App { @@ -113,7 +113,7 @@ The animal's name is: Boots ### Инвариантность -Обобщенные классы в Scala по умолчанию являются инвариантными. Это означает, что они не являются ни ковариантными, ни контрвариантными друг другу. В контексте следующего примера класс `Container` является инвариантным. Между `Container[Cat]` и `Container[Animal]`, нет ни прямой и ни обратной взаимосвязи. +Обобщенные классы в Scala по умолчанию являются инвариантными. Это означает, что они не являются ни ковариантными, ни контрвариантными друг другу. В контексте следующего примера класс `Container` является инвариантным. Между `Container[Cat]` и `Container[Animal]`, нет ни прямой, ни обратной взаимосвязи. ```tut class Container[A](value: A) { @@ -131,7 +131,7 @@ class Container[A](value: A) { val catContainer: Container[Cat] = new Container(Cat("Felix")) val animalContainer: Container[Animal] = catContainer animalContainer.setValue(Dog("Spot")) -val cat: Cat = catContainer.getValue // Ой, мы бы закончили собакой присвоенной к коту. +val cat: Cat = catContainer.getValue // Ой, мы бы закончили присвоением собаки к коту. ``` К счастью, компилятор остановит нас прежде, чем мы зайдем так далеко. @@ -151,4 +151,4 @@ case class Mouse(name: String) extends SmallAnimal ### Сравнение с другими языками -В языках, похожих на Scala, разные способы поддержи вариантности. Например, указания вариантности в Скале очень похожи на то как это делается в C#, где такие указания добавляются при объявлении абстракции класса (вариантность при объявлении). В Java, однако, указание вариантности задается клиентами класса непосредственно при использовании абстракции класса (вариантность при использовании). +В языках, похожих на Scala, разные способы поддержи вариантности. Например, указания вариантности в Scala очень похожи на то, как это делается в C#, где такие указания добавляются при объявлении абстракции класса (вариантность при объявлении). Однако в Java, указание вариантности задается непосредственно при использовании абстракции класса (вариантность при использовании). From 496cac96a2c2ff01966102fb945d46edadf46553 Mon Sep 17 00:00:00 2001 From: "Kotobotov.ru" Date: Sat, 2 Feb 2019 02:20:34 +0700 Subject: [PATCH 4/5] :ok_hand: Updating for-comprehensions and generic-classes. Took 1 hour 9 minutes --- _ru/tour/for-comprehensions.md | 10 +++++----- _ru/tour/generic-classes.md | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/_ru/tour/for-comprehensions.md b/_ru/tour/for-comprehensions.md index 6c825e2354..5c79ca062c 100644 --- a/_ru/tour/for-comprehensions.md +++ b/_ru/tour/for-comprehensions.md @@ -1,6 +1,6 @@ --- layout: tour -title: Представление вида For +title: Сложные for-выражения discourse: true @@ -13,7 +13,7 @@ previous-page: extractor-objects --- -Scala предлагает простую запись для выражения *последовательных преобразований*. Такие преобразования представлены в виде `for синтаксиса` и записывается как `for (enumerators) yield e`, где `enumerators` относятся к списку перечислений, разделенных точкой с запятой. Отдельный элемент (*enumerator*) является либо генератором, который вводит новые переменные, либо фильтром. Представление for вычисляет тело `e` (которое связанно с тем что генерирует *enumerator*) и возвращает последовательность вычислений. +Scala предлагает простую запись для выражения *последовательных преобразований*. Эти преобразования можно упростить используя специальный синтаксис `for выражения` (for comprehension), который записывается как `for (enumerators) yield e`, где `enumerators` относятся к списку перечислителей, разделенных точкой с запятой. Где отдельный такой "перечислитель" (*enumerator*) является либо генератором, который вводит новые переменные, либо фильтром. For-выражение вычисляет тело `e` (которое связанно с тем что генерирует *enumerator*) и возвращает последовательность вычислений. Вот пример: @@ -30,7 +30,7 @@ val twentySomethings = for (user <- userBase if (user.age >=20 && user.age < 30) twentySomethings.foreach(name => println(name)) // выводит "Travis Dennis" ``` -Представление `for`, используется с оператором `yield`, на самом деле создает `List`. Потому что мы указали `yield user.name` (то есть вывести имя пользователя), получаем `List[String]`. `user <- userBase` и есть наш генератор, а `if (user.age >=20 && user.age < 30)` - это фильтр который отфильтровывает пользователей, не достигших 20-летнего возраста. + `for`-выражение, используется с оператором `yield`, на самом деле создает `List`. Потому что мы указали `yield user.name` (то есть вывести имя пользователя), получаем `List[String]`. `user <- userBase` и есть наш генератор, а `if (user.age >=20 && user.age < 30)` - это фильтр который отфильтровывает пользователей, не достигших 20-летнего возраста. Ниже приведен более сложный пример использования двух генераторов. Он вычисляет все пары чисел между `0` и `n-1`, сумма которых равна заданному значению `v`: @@ -52,9 +52,9 @@ foo(10, 10) foreach { (0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6) (0, 7) (0, 8) (0, 9) (1, 1) ... ``` -Обратите внимание, что for представление не ограничивается только работой со списками. Каждый тип данных, поддерживающий операции `withFilter`, `map`, and `flatMap` (с соответствующими типами), может быть использован в for представлении. +Обратите внимание, что for-выражение не ограничивается только работой со списками. Каждый тип данных, поддерживающий операции `withFilter`, `map`, and `flatMap` (с соответствующими типами), может быть использован в for-выражении. -Вы можете обойтись без `yield` в for представлении. В таком случае, результатом будет `Unit`. Такое может быть полезным для выполнения кода основанного на побочных эффектах. Вот программа, эквивалентная предыдущей, но без использования `yield`: +Вы можете обойтись без `yield` в for-выражении. В таком случае, результатом будет `Unit`. Это может быть полезным для выполнения кода основанного на побочных эффектах. Вот программа, эквивалентная предыдущей, но без использования `yield`: ```tut def foo(n: Int, v: Int) = diff --git a/_ru/tour/generic-classes.md b/_ru/tour/generic-classes.md index ab74579e99..03487c1634 100644 --- a/_ru/tour/generic-classes.md +++ b/_ru/tour/generic-classes.md @@ -13,10 +13,10 @@ previous-page: for-comprehensions assumed-knowledge: classes unified-types --- -Обобщенные классы - это классы, которые принимают тип в качестве параметра. Они особенно полезны для создания коллекций классов. +Обобщенные классы (Generic classes) - это классы, обладающие параметрическим полиморфизмом (т. е. классы, которые изменяют свое поведение в зависимости от приписываемого им типа. Этот тип указывается в квадратных скобках `[]` сразу после имени класса). Они особенно полезны для создания коллекций. ## Объявление обобщенного класса -Обобщенные классы принимают тип в качестве параметра в квадратных скобках `[]`. По соглашению обычно используют буквы `A` в качестве имени параметра типа, хотя можно использовать любое имя. +Для объявления обобщенного класса необходимо после имени добавить еще один параметр "тип класса" в квадратных скобках `[]`. По соглашению обычно используют заглавные буквы `A` в качестве указания, на новый параметр "тип класса", хотя можно использовать любое имя. ```tut class Stack[A] { private var elements: List[A] = Nil @@ -33,7 +33,7 @@ class Stack[A] { ## Использование -Чтобы использовать обобщенный класс, поместите тип в квадратные скобки вместо `A`. +Чтобы использовать обобщенный класс, поместите конкретный тип в квадратные скобки вместо `A`. ``` val stack = new Stack[Int] stack.push(1) From b10fbcf0d4735b05ffaf8bde5f61928d4853158c Mon Sep 17 00:00:00 2001 From: "Kotobotov.ru" Date: Tue, 5 Feb 2019 11:27:04 +0700 Subject: [PATCH 5/5] add original with brackets + improve style Took 2 hours 14 minutes --- _ru/tour/case-classes.md | 2 +- _ru/tour/extractor-objects.md | 4 ++-- _ru/tour/generic-classes.md | 2 +- _ru/tour/mixin-class-composition.md | 2 +- _ru/tour/package-objects.md | 2 +- _ru/tour/packages-and-imports.md | 4 ++-- _ru/tour/pattern-matching.md | 14 +++++++------- _ru/tour/regular-expression-patterns.md | 2 +- _ru/tour/self-types.md | 4 ++-- _ru/tour/singleton-objects.md | 22 +++++++++++----------- _ru/tour/tour-of-scala.md | 2 +- _ru/tour/traits.md | 2 +- _ru/tour/tuples.md | 16 ++++++++-------- _ru/tour/variances.md | 4 ++-- 14 files changed, 41 insertions(+), 41 deletions(-) diff --git a/_ru/tour/case-classes.md b/_ru/tour/case-classes.md index 6d5b07a196..13c3622587 100644 --- a/_ru/tour/case-classes.md +++ b/_ru/tour/case-classes.md @@ -14,7 +14,7 @@ prerequisite-knowledge: classes, basics, mutability --- -Классы образцы похожи на обычные классы с несколькими ключевыми отличиями, о которых мы поговорим ниже. Классы образцы хороши для моделирования неизменяемых данных. На следующем странице обзора мы увидим, насколько они полезны для участия в [сопоставлении с примером](pattern-matching.html). +Классы образцы (Case classes) похожи на обычные классы с несколькими ключевыми отличиями, о которых мы поговорим ниже. Классы образцы хороши для моделирования неизменяемых данных. На следующей странице обзора вы увидите, насколько они полезны для участия в [сопоставлении с примером](pattern-matching.html). ## Объявление класса образца Минимальный вариант объявления класса образца: указание ключевого слова `case class`, название и список параметров (которые могут быть пустыми). Пример: diff --git a/_ru/tour/extractor-objects.md b/_ru/tour/extractor-objects.md index 46d3ec1805..d3824e69c3 100644 --- a/_ru/tour/extractor-objects.md +++ b/_ru/tour/extractor-objects.md @@ -1,6 +1,6 @@ --- layout: tour -title: Извлекающий Объект +title: Объект Экстрактор discourse: true @@ -13,7 +13,7 @@ previous-page: regular-expression-patterns --- -Извлекающий Объект - это объект с методом `unapply`. В то время как метод `apply` обычно действует как конструктор, который принимает аргументы и создает объект, метод `unapply` действует обратным образом, он принимает объект и пытается вернуть аргументы из которых он (возможно) был создан. Чаще всего этот метод используется в функциях сопоставления с примером и в частично определенных функциях. +Объект Экстрактор (объект распаковщик или extractor object) - это объект с методом `unapply`. В то время как метод `apply` обычно действует как конструктор, который принимает аргументы и создает объект, метод `unapply` действует обратным образом, он принимает объект и пытается извлечь и вернуть аргументы из которых он (возможно) был создан. Чаще всего этот метод используется в функциях сопоставления с примером и в частично определенных функциях. ```tut import scala.util.Random diff --git a/_ru/tour/generic-classes.md b/_ru/tour/generic-classes.md index 03487c1634..30587567ca 100644 --- a/_ru/tour/generic-classes.md +++ b/_ru/tour/generic-classes.md @@ -16,7 +16,7 @@ assumed-knowledge: classes unified-types Обобщенные классы (Generic classes) - это классы, обладающие параметрическим полиморфизмом (т. е. классы, которые изменяют свое поведение в зависимости от приписываемого им типа. Этот тип указывается в квадратных скобках `[]` сразу после имени класса). Они особенно полезны для создания коллекций. ## Объявление обобщенного класса -Для объявления обобщенного класса необходимо после имени добавить еще один параметр "тип класса" в квадратных скобках `[]`. По соглашению обычно используют заглавные буквы `A` в качестве указания, на новый параметр "тип класса", хотя можно использовать любое имя. +Для объявления обобщенного класса необходимо после имени добавить тип в квадратных скобках `[]` как еще один параметр класса. По соглашению обычно используют заглавные буквы `A`, хотя можно использовать любые имена. ```tut class Stack[A] { private var elements: List[A] = Nil diff --git a/_ru/tour/mixin-class-composition.md b/_ru/tour/mixin-class-composition.md index 06c982cc26..4c528ca3c9 100644 --- a/_ru/tour/mixin-class-composition.md +++ b/_ru/tour/mixin-class-composition.md @@ -13,7 +13,7 @@ previous-page: tuples prerequisite-knowledge: inheritance, traits, abstract-classes, unified-types --- -Примеси - это трейты, которые используются для создания класса. +Примеси (Mixin) - это трейты, которые используются для создания класса. ```tut abstract class A { diff --git a/_ru/tour/package-objects.md b/_ru/tour/package-objects.md index 35cab8b921..43cc9c85da 100644 --- a/_ru/tour/package-objects.md +++ b/_ru/tour/package-objects.md @@ -13,7 +13,7 @@ previous-page: packages-and-imports # Объекты Пакета -У каждого пакета может существовать связанный с этим пакетом объект, общий для всех членов пакета. Такой объект может быть только один. Любые выражения, содержащиеся в объекте пакета, считаются членами самого пакета. +У каждого пакета может существовать связанный с этим пакетом объект (package object), общий для всех членов пакета. Такой объект может быть только один. Любые выражения, содержащиеся в объекте пакета, считаются членами самого пакета. Объекты пакета могут содержать произвольные виды выражений, а не только переменные и методы. Например, они часто используются для хранения псевдонимов типа и наборов неявных преобразований доступных всему пакету. Объекты пакета могут также наследоваться от классов и трейтов Scala. diff --git a/_ru/tour/packages-and-imports.md b/_ru/tour/packages-and-imports.md index ed7a0137df..ef7865e3e7 100644 --- a/_ru/tour/packages-and-imports.md +++ b/_ru/tour/packages-and-imports.md @@ -13,7 +13,7 @@ next-page: package-objects --- # Пакеты и Импорт -Scala использует пакеты для создания пространства имен, которые открывают путь к модульной структуре кода. +Scala использует пакеты для указания пространства имен, они позволяют создавать модульную структуру кода. ## Создание пакета Пакеты создаются путем объявления одного или нескольких имен пакетов в верхней части файла Scala. @@ -59,7 +59,7 @@ class Lens Что может соответствовать следующей структуре каталога: `SelfDrivingCar/src/main/scala/com/google/selfdrivingcar/camera/Lens.scala`. ## Импорт -Указание `import` открывает доступ к членам (классам, признакам, функциям и т.д.) в других пакетах. Указание `import` не требуется для доступа к членам одного и того же пакета. Указание `import` избирательны: +Указание `import` открывает доступ к членам (классам, трейтам, функциям и т.д.) в других пакетах. Указание `import` не требуется для доступа к членам одного и того же пакета. Указание `import` избирательны: ``` import users._ // групповой импорт всего пакета users import users.User // импортировать только User diff --git a/_ru/tour/pattern-matching.md b/_ru/tour/pattern-matching.md index 50aa1e4638..9a46a74005 100644 --- a/_ru/tour/pattern-matching.md +++ b/_ru/tour/pattern-matching.md @@ -14,10 +14,10 @@ prerequisite-knowledge: case-classes, string-interpolation, subtyping --- -Сопоставление с примером - это механизм сравнения значений с определенным примером. При успешном совпадении значение может быть разложено на составные части. Мы рассматриваем сопоставление с примером, как более мощную версию `switch` оператора из Java. Eго также можно использовать вместо серии if/else выражений. +Сопоставление с примером (Pattern matching) - это механизм сравнения значений с определенным примером. При успешном совпадении значение может быть разложено на составные части. Мы рассматриваем сопоставление с примером, как более мощную версию `switch` оператора из Java. Eго также можно использовать вместо серии if/else выражений. ## Синтаксис -Сопоставляющее выражение имеет значение, ключевое слово `match` и по крайней мере, один пункт `case`. +Синтаксис сопоставления с примером состоит из значения, ключевого слова `match` (сопоставить) и по крайней мере, одного пункта с примером `case`, с которым мы хотим сопоставить наше значение. ```tut import scala.util.Random @@ -30,7 +30,7 @@ x match { case _ => "many" } ``` -Значение константы `x` выше представляет собой случайное целое число от 0 до 10. `x` становится левым операндом оператора `match`, а справа - выражением с четырьмя образцами (называемые еще _вариантами_). Последний вариант `_` - позволяет "поймать все оставшиеся варианты" т. е. для любого числа больше 2. +Значение константы `x` выше представляет собой случайное целое число от 0 до 10. `x` становится левым операндом оператора `match`, а справа - выражением с четырьмя примерами (называемые еще _вариантами_). Последний вариант `_` - позволяет "поймать все оставшиеся варианты" т. е. для любого числа больше 2. Сопоставление с примером возвращает значение. ```tut @@ -46,7 +46,7 @@ matchTest(1) // one ## Сопоставление с классами образцами -Классы образцы особенно полезны при сопоставление с примером. +Классы образцы особенно полезны для сопоставления. ```tut abstract class Notification @@ -60,7 +60,7 @@ case class VoiceRecording(contactName: String, link: String) extends Notificatio ``` `Notification` - абстрактный суперкласс, от которого наследуются три конкретных типа реализаций классов образцов `Email`, `SMS`, и `VoiceRecording`. Теперь мы можем делать сопоставление с примером используя в качестве примера один из этих классов образцов. -При сопоставлении с классом образцом мы можем сразу извлекать параметры из которых состоит класс (благодаря автоматическому использованию [извлекающего объекта](extractor-objects.html)): +При сопоставлении с классом образцом мы можем сразу извлекать параметры из которых состоит класс (благодаря автоматическому использованию [объекта экстрактора](extractor-objects.html)): ``` def showNotification(notification: Notification): String = { @@ -83,7 +83,7 @@ println(showNotification(someVoiceRecording)) // выводит "you received a Функция `showNotification` принимает в качестве параметра абстрактный тип `Notification` который проверяет по образцам (т.е. выясняет, является ли он классом `Email`, `SMS` или `VoiceRecording`). В `case Email(email, title, _)`поля `email` и `title` используются в возвращаемом значении, а вот поле `body` игнорируется благодаря символу `_`. ## Ограждения примеров -Ограждения примеров - это просто логические выражения, которые используются для того, чтобы сделать выбор более специфичным. Просто добавьте `if <логическое выражение>` после примера. +Ограждения примеров - это просто логические выражения, которые используются для того, чтобы сделать выбор более специфичным (убрать лишние варианты). Просто добавьте `if <логическое выражение>` после примера. ``` def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = { @@ -148,4 +148,4 @@ def findPlaceToSit(piece: Furniture): String = piece match { ## Замечания Сопоставление с примером наиболее полезно для сопоставления алгебраических типов, выраженных через [классы образцы](case-classes.html). -Scala также позволяет создавать образцы независимо от классов образцов, через использование метода `unapply` в [извлекающих объектах](extractor-objects.html). +Scala также позволяет создавать образцы независимо от классов образцов, через использование метода `unapply` в [объектах экстракторах](extractor-objects.html). diff --git a/_ru/tour/regular-expression-patterns.md b/_ru/tour/regular-expression-patterns.md index eeb8b39312..32877a0106 100644 --- a/_ru/tour/regular-expression-patterns.md +++ b/_ru/tour/regular-expression-patterns.md @@ -13,7 +13,7 @@ previous-page: singleton-objects --- -Регулярные выражения - это строки, которые задают шаблон для поиска данных. Любая строка может быть преобразована в регулярное выражение методом `.r`. +Регулярные выражения (Regular expression) - это специальный шаблон для поиска данных задаваемый в виде текстовой строки. Любая строка может быть преобразована в регулярное выражение методом `.r`. ```tut import scala.util.matching.Regex diff --git a/_ru/tour/self-types.md b/_ru/tour/self-types.md index 34ea641049..ccc896b0ec 100644 --- a/_ru/tour/self-types.md +++ b/_ru/tour/self-types.md @@ -1,6 +1,6 @@ --- layout: tour -title: Самоописываемый тип +title: Самоописываемые типы discourse: true @@ -14,7 +14,7 @@ topics: self-types prerequisite-knowledge: nested-classes, mixin-class-composition --- -Самоописываемый тип - это способ объявить, что трейт должен быть смешан с другим трейтом, даже если он не расширяет его напрямую. Что открывает доступ к членам зависимости без импортирования. +Самоописываемый тип(Self type) - это способ объявить, что трейт должен быть смешан с другим трейтом, даже если он не расширяет его напрямую. Что открывает доступ к членам зависимости без импортирования. Самоописываемый тип - это способ сузить тип `this` или другого идентификатора, который ссылается на `this`. Синтаксис похож на синтаксис обычной функции, но означает кое-что иное. diff --git a/_ru/tour/singleton-objects.md b/_ru/tour/singleton-objects.md index 0bc752d1f2..eab6688c75 100644 --- a/_ru/tour/singleton-objects.md +++ b/_ru/tour/singleton-objects.md @@ -1,6 +1,6 @@ --- layout: tour -title: Одноэлементные Объекты +title: Объекты Одиночки discourse: true @@ -12,13 +12,13 @@ next-page: regular-expression-patterns previous-page: pattern-matching prerequisite-knowledge: classes, methods, private-methods, packages, option --- -Объект - это класс, существующий в единственном экземпляре. Он создается лениво, когда на него ссылаются, также как ленивые значения (lazy val). +Все объекты являются одиночками (Singleton Object) - то есть существуют в единственном экземпляре. Он создается лениво, когда на него ссылаются, также как ленивые значения (lazy val). -На самом верхнем уровне объект является одноэлементным. +На самом верхнем уровне объект является одиночкой. Как член класса или как локальная переменная, он ведет себя точно так же как ленивое значение (lazy val). -# Объявление одноэлементного объекта -Объект - это значение. Объявление объекта происходит похожим с классом образом, но используется ключевое слово `object`: +# Объявление одиночного объекта +Объект - является значением. Объявление объекта происходит схожим с классом образом, но используется ключевое слово `object`: ```tut object Box ``` @@ -31,7 +31,7 @@ object Logger { def info(message: String): Unit = println(s"INFO: $message") } ``` -Метод `info` может быть импортирован из любой точки программы. Создание подобных методов является распространенным вариантом использования одноэлементных объектов. +Метод `info` может быть импортирован в любом месте программы. Создание подобных методов является распространенным вариантом использования одиночных объектов. Давайте посмотрим, как использовать `info` в другом пакете: @@ -49,11 +49,11 @@ class Test { Метод `info` виден благодаря указанию импорта `import logging.Logger.info`. -Замечание: Если `object` не является объектом верхнего уровня, но вложен в другой класс или объект, то объект, как и любой другой член, "зависим от пути". Это означает, что при двух видах напитков, `class Milk` and `class OrangeJuice`, член класса `object NutritionInfo` "зависит" от окружающего класса, либо milk либо orange juice. `milk.NutritionInfo` полностью отделен от `orangejuice.NutritionInfo`. +Замечание: Если `object` не является объектом верхнего уровня, но вложен в другой класс или объект, то объект, как и любой другой член, "зависим от пути". ## Объекты компаньоны -Объект с тем же именем, что и класс, называется _объект компаньон_. И наоборот, класс является классом-компаньоном объекта. Класс или объект компаньон может получить доступ к приватным членам своего спутника. Используйте объект компаньон для методов и значений, которые не специфичны для экземпляров класса компаньона. +Объект с тем же именем, что и класс называется _объект компаньон_ (companion object). И наоборот, класс является классом-компаньоном объекта. Класс или объект компаньон может получить доступ к приватным членам своего спутника. Используйте объект компаньон для методов и значений, которые не специфичны для экземпляров класса компаньона. ``` import scala.math._ @@ -71,9 +71,9 @@ val circle1 = new Circle(5.0) circle1.area ``` -Класс `Circle` имеет член `area`, который специфичен для каждого экземпляра, и одноэлементный `object Circle` с методом `calculateArea`, который доступен для каждого экземпляра. +Класс `Circle` имеет член `area`, который специфичен для каждого конкретного экземпляра, а метод `calculateArea` одиночного объекта `Circle`, доступен для каждого экземпляра класса `Circle`. -Объект компаньон может также содержать методы производящие экземпляры класса спутника: +Объект компаньон может также содержать методы создающие конкретные экземпляры класса спутника: ```tut class Email(val username: String, val domainName: String) @@ -96,7 +96,7 @@ scalaCenterEmail match { case None => println("Error: could not parse email") } ``` -`object Email` содержит производящий метод `fromString`, который создает экземпляр `Email` из строки. Мы возвращаем результат как опцию `Option[Email]` на случай возникновения ошибок парсинга. +`object Email` содержит производящий метод `fromString`, который создает экземпляр `Email` из строки. Мы возвращаем результат как `Option[Email]` на случай возникновения ошибок парсинга. Примечание: Если у класса или объекта есть компаньон, они должны быть размещены в одном и том же файле. Чтобы задать компаньонов в REPL, либо определите их на той же строке, либо перейдите в режим `:paste`. diff --git a/_ru/tour/tour-of-scala.md b/_ru/tour/tour-of-scala.md index 7fcd19a9c9..a2e6cc805f 100644 --- a/_ru/tour/tour-of-scala.md +++ b/_ru/tour/tour-of-scala.md @@ -27,7 +27,7 @@ Scala - это чистый объектно-ориентированный яз ## Scala функциональный ## Scala также является функциональным языком в том смысле, что [каждая функция - это значение](unified-types.html). Scala предоставляет [легкий синтаксис](basics.html) для определения анонимных функций, поддерживает [функции высшего порядка](higher-order-functions.html), поддерживает [вложенные функции](nested-functions.html), а также [каррирование](multiple-parameter-lists.html). Scala имеют встроенную поддержку алгебраических типов данных, которые используются в большинстве функциональных языках программирования (эта поддержка базируется на механизме [сопоставления с примером](pattern-matching.html), где в качестве примера выступают [классы образцы](case-classes.html) ). [Объекты](singleton-objects.html) предоставляют удобный способ группировки функций, не входящих в класс. -Вдобавок к этому, концепция сопоставления с примером логично переносится на [обработку XML-данных](https://github.com/scala/scala-xml/wiki/XML-Processing) используя в качестве примера [регулярные выражения](regular-expression-patterns.html), при поддержке функционала [извлекающих объектов](extractor-objects.html). Для еще большего удобства обработки данных представлена схема формирования запросов с использованием [for-выражения](for-comprehensions.html). Такие возможности делают Scala идеальным решением для разработки приложений по типу веб-сервисов. +Вдобавок к этому, концепция сопоставления с примером логично переносится на [обработку XML-данных](https://github.com/scala/scala-xml/wiki/XML-Processing) используя в качестве примера [регулярные выражения](regular-expression-patterns.html), при поддержке функционала [объектов экстракторов](extractor-objects.html). Для еще большего удобства обработки данных представлена схема формирования запросов с использованием [for-выражения](for-comprehensions.html). Такие возможности делают Scala идеальным решением для разработки приложений по типу веб-сервисов. ## Scala статически типизированный ## Scala оснащен выразительной системой типов, которая обеспечивает безопасное и гармоничное использование абстракций. В частности, система типов поддерживает: diff --git a/_ru/tour/traits.md b/_ru/tour/traits.md index 538f18422e..4fea28e80f 100644 --- a/_ru/tour/traits.md +++ b/_ru/tour/traits.md @@ -15,7 +15,7 @@ prerequisite-knowledge: expressions, classes, generics, objects, companion-objec --- -Трейты используются чтоб обмениваться между классами информацией о структуре и полях. Они похожи на интерфейсы из Java 8. Классы и объекты могут расширять трейты, но трейты не могут быть созданы и поэтому не имеют параметров. +Трейты (Traits) используются чтоб обмениваться между классами информацией о структуре и полях. Они похожи на интерфейсы из Java 8. Классы и объекты могут расширять трейты, но трейты не могут быть созданы и поэтому не имеют параметров. ## Объявление трейта Минимальное объявление трейта - это просто ключевое слово `trait` и его имя: diff --git a/_ru/tour/tuples.md b/_ru/tour/tuples.md index 09bca25443..094a76fbdf 100644 --- a/_ru/tour/tuples.md +++ b/_ru/tour/tuples.md @@ -1,6 +1,6 @@ --- layout: tour -title: Кортеж +title: Кортежи discourse: true @@ -14,7 +14,7 @@ topics: tuples --- -В Scala, a кортеж это класс контейнер содержащий упорядоченный набор элементов различного типа. +В Scala, кортеж (Тuple) это класс контейнер содержащий упорядоченный набор элементов различного типа. Кортежи неизменяемы. Кортежи могут пригодиться, когда нам нужно вернуть сразу несколько значений из функции. @@ -34,7 +34,7 @@ val ingredient = ("Sugar" , 25):Tuple2[String, Int] ## Доступ к элементам Доступ к элементам кортежа осуществляется при помощи синтаксиса подчеркивания. -'tuple._n' дает n-ый элемент (до тех пор, сколько существует элементов). +'tuple._n' дает n-ый элемент (столько, сколько существует элементов). ```tut println(ingredient._1) // Sugar @@ -42,9 +42,9 @@ println(ingredient._1) // Sugar println(ingredient._2) // 25 ``` -## Разборка данных кортежа +## Распаковка данных кортежа -Scala кортежи также поддерживают разборку. +Scala кортежи также поддерживают [распаковку](extractor-objects.html). ```tut val (name, quantity) = ingredient @@ -54,7 +54,7 @@ println(name) // Sugar println(quantity) // 25 ``` -Разборка данных кортежа может быть использована в [сопоставлении с примером](pattern-matching.html) +Распаковка данных кортежа может быть использована в [сопоставлении с примером](pattern-matching.html) ```tut val planetDistanceFromSun = List(("Mercury", 57.9), ("Venus", 108.2), ("Earth", 149.6 ), ("Mars", 227.9), ("Jupiter", 778.3)) @@ -78,7 +78,7 @@ planetDistanceFromSun.foreach{ tuple => { } ``` -Или в ['for' представлении](for-comprehensions.html). +Или в ['for' выражении](for-comprehensions.html). ```tut val numPairs = List((2, 5), (3, -7), (20, 56)) @@ -92,4 +92,4 @@ for ((a, b) <- numPairs) { Значение () типа Unit по свой сути совпадает со значением () типа Tuple0. Может быть только одно значение такого типа, так как в нём нет элементов. -Иногда бывает трудно выбирать между кортежами и классами образцами. Как правило, классы образцы являются предпочтительным выбором, если сам класс-контейнер содержащий элементы сам по себе имеет значимый смысл. +Иногда бывает трудно выбирать между кортежами и классами образцами. Как правило, классы образцы являются предпочтительным выбором, если класс-контейнер содержащий элементы сам по себе имеет значимый смысл. diff --git a/_ru/tour/variances.md b/_ru/tour/variances.md index 15f4604457..4b3a1d4a3f 100644 --- a/_ru/tour/variances.md +++ b/_ru/tour/variances.md @@ -13,7 +13,7 @@ previous-page: generic-classes --- -Вариантность - это указание определенной специфики взаимосвязи между сложными связанными типам. Scala поддерживает вариантную аннотацию типов у [обобщенных классов](generic-classes.html), что позволяет им быть ковариантными, контрвариантными или инвариантными (если нет никакого указание на вариантность). Использование вариантности в системе типов позволяет устанавливать понятные взаимосвязи между сложными типами, в то время как отсутствие вариантности может ограничить повторное использование абстракции класса. +Вариантность (Variances) - это указание определенной специфики взаимосвязи между связанными типам. Scala поддерживает вариантную аннотацию типов у [обобщенных классов](generic-classes.html), что позволяет им быть ковариантными, контрвариантными или инвариантными (если нет никакого указание на вариантность). Использование вариантности в системе типов позволяет устанавливать понятные взаимосвязи между сложными типами, в то время как отсутствие вариантности может ограничить повторное использование абстракции класса. ```tut class Foo[+A] // ковариантный класс @@ -23,7 +23,7 @@ class Baz[A] // инвариантными класс ### Ковариантность -Параметр типа `A` обобщенного класса можно сделать ковариантным с помощью аннотации `+A`. Для некоторого класса `List[+A]`, указание `A` в виде коварианта подразумевает, что для двух типов `A` и `B`, где `A` является подтипом `B`, `List[A]` представляет собой подтип `List[B]`. Что позволяет нам создавать очень полезные и интуитивно понятные взаимоотношения между типами с использованием обобщений (дженериков). +Параметр типа `A` обобщенного класса можно сделать ковариантным с помощью аннотации `+A`. Для некоторого класса `List[+A]`, указание `A` в виде коварианта подразумевает, что для двух типов `A` и `B`, где `A` является подтипом `B`, `List[A]` представляет собой подтип `List[B]`. Что позволяет нам создавать очень полезные и интуитивно понятные взаимоотношения между типами с использованием обобщений (generics). Рассмотрим простую структуру классов: