Scala в примерах: различия между версиями

Содержимое удалено Содержимое добавлено
→‎Кортежи: — перевод параграфа
→‎Функции: — перевод параграфа
Строка 962:
 
== Функции ==
 
Scala — функциональный язык в том смысле, что функции — значения первого порядка. Scala также объектно-ориентированный язык в том смысле, что каждое значение является объектом. Из этого следует, что функции в Scala являются объектами. Например, функция из типа <tt>String</tt> в тип <tt>Int</tt> представлена экземпляром трейта <tt>Function1[String, Int]</tt>. Трейт <tt>Function1</tt> определен так:
 
<font size=3><syntaxhighlight lang=Scala>
package scala
trait Function1[-A, +B] {
def apply(x: A): B
}
</syntaxhighlight></font>
 
Помимо <tt>Function1</tt> есть также определения для функций любой другой арности (нынешняя реализация устанавливает некий разумный предел). То есть, для любого возможного числа параметров есть соответствующее определение. Синтаксис типа функции в Scala ''(T<sub>1</sub>, ..., T<sub>n</sub>) => S'' — это просто сокращение для параметризованого типа <tt>Function</tt>''n[T<sub>1</sub>, ..., T<sub>n</sub>, S]''.
 
Scala использует одинаковый синтаксис ''f(x)'' для применения функции, вне зависимости от того, является ли ''f'' методом или функциональным объектом. Это возможно из-за следующей конвенции: применение функции ''f(x)'', где ''f'' — объект (в противоположность методу) это укороченная запись для ''f''.<tt>apply</tt>''(x)''. Метод <tt>apply</tt>, принадлежащий типу функций, вставляется автоматически, где необходимо.
 
Поэтому мы определяли операцию взятия элемента из массива в [[Scala в примерах#Аннотации вариантности|параграфе 8.2]] при помощи метода <tt>apply</tt>. Для любого масива <tt>a</tt> операция взятия элемента <tt>a(i)</tt> это укороченная запись <tt>a.apply(i)</tt>.
 
Функции — это пример того, как полезны объявления контравариантных типовых параметров. Рассмотрим, например, следующий код:
 
<font size=3><syntaxhighlight lang=Scala>
val f: (AnyRef => Int) = x => x.hashCode()
val g: (String => Int) = f
g("abc")
</syntaxhighlight></font>
 
Присваивать значение <tt>g</tt> типа <tt>String => Int</tt> величине <tt>f</tt> типа <tt>AnyRef => Int</tt> — корректно. Действительно, все, что можно сделать с функцией типа <tt>String => Int</tt> — это передать ей строку, чтобы получить целое число. Очевидно, что то же самое верно для функции <tt>f</tt>: если мы передадим ей строку (или любой другой объект), мы получим целое число. Это показывает, что подтипирование функций контравариантно по типу аргумента, тогда как оно ковариантно по типу результата. Короче говоря, ''S => T'' это подтип ''S‘ => T‘'', если ''S‘'' — подтип ''S'' и ''T'' — подтип ''T‘''.
 
'''Пример 8.6.1''' Рассмотрим код:
 
<font size=3><syntaxhighlight lang=Scala>
val plus1: (Int => Int) = (x: Int) = > x +1
plus1(2)
</syntaxhighlight></font>
 
Эта запись может быть развернута так:
 
<font size=3><syntaxhighlight lang=Scala>
val plus1: Function1[Int, Int] = new Function1[Int, Int] {
def apply(x: Int): Int = x + 1
}
plus1.apply(2)
</syntaxhighlight></font>
 
Здесь создание объекта <tt>'''new''' Function1[Int, Int]{ … }</tt> представляет собой сущность ''анонимного класса''. Оно сочетает создание новой объекта <tt>Function1</tt> с реализацией метода <tt>apply</tt> (который абстрактен в <tt>Function1</tt>). Эквивалентно, но более многословно можно было бы использовать локальный класс:
 
<font size=3><syntaxhighlight lang=Scala>
val plus1: Function1[Int, Int] = {
class Local extends Function1[Int, Int] {
def apply(x: Int): Int = x + 1
}
new Local: Function1[Int, Int] }
plus1.apply(2)
</syntaxhighlight></font>
 
= Списки =