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

Содержимое удалено Содержимое добавлено
→‎Каррирование: — закончен перевод параграфа
Строка 681:
}
</syntaxhighlight></font>
 
В этой формулировке <tt>sum</tt> — это функция, которая возвращает другую функцию, а именно специализированную суммирующую функцию <tt>sumF</tt>. Эта последняя функция выполняет всю работу: она принимает границы <tt>a</tt> и <tt>b</tt> как параметры, применяет <tt>f</tt>, параметр <tt>sum</tt>, ко всем целым числам между ними, и суммирует результаты.
 
При помощи этой формулировки <tt>sum</tt> мы можем теперь определить:
 
<font size=3><syntaxhighlight lang=Scala>
def sumInts = sum(x => x)
def sumSquares = sum(x => x * x)
def sumPowersOfTwo = sum(powerOfTwo)
</syntaxhighlight></font>
 
Или, эквивалентно тому, с такими определениями:
 
<font size=3><syntaxhighlight lang=Scala>
val sumInts = sum(x => x)
val sumSquares = sum(x => x * x)
val sumPowersOfTwo = sum(powerOfTwo)
</syntaxhighlight></font>
 
<tt>sumInts</tt>, <tt>sumSquares</tt> и <tt>sumPowersOfTwo</tt> можно применять как любые другие функции. Например,
 
<font size=3><syntaxhighlight lang=Scala>
scala> sumSquares(1, 10) + sumPowersOfTwo(10, 20)
unnamed0: Int = 2096513
</syntaxhighlight></font>
 
Как же применяются функции, возвращающие функции? Например, в этом выражении
 
<font size=3><syntaxhighlight lang=Scala>
sum(x => x * x)(1, 10)
</syntaxhighlight></font>
 
функция <tt>sum</tt> применяется к функции возведения в квадрат <tt>(x => x * x)</tt>. Получившаяся функция затем применяется ко второму списку аргументов <tt>(1, 10)</tt>.
 
Такая запись возможна, потому что применение функций левоассоциативно. То есть, если args<sub>1</sub> и args<sub>2</sub> — списки аргументов, то ''f''(args<sub>1</sub>)(args<sub>2</sub>) эквивалентно (''f''(args<sub>1</sub>))(args<sub>2</sub>).
 
В нашем примере <tt>sum(x => x * x)(1, 10)</tt> эквивалентно следующему выражению: <tt>(sum(x => x * x))(1, 10)</tt>.
 
Функций, возвращающие функции, насколько полезны, что у Scala есть для них специальный синтаксис. Например, следующее определение <tt>sum</tt> эквивалентно предыдущему, но оно короче:
 
<font size=3><syntaxhighlight lang=Scala>
def sum(f: Int => Int)(a: Int, b: Int): Int =
if (a > b) 0 else f(a) + sum(f)(a + 1, b)
</syntaxhighlight></font>
 
Более общее определение каррированной функции:
 
<code>
'''def''' f (args<sub>1</sub>) … (args<sub>n</sub>) = E
</code>
 
где <tt>''n'' > 1</tt> раскрывается в
 
<code>
'''def''' f (args<sub>1</sub>) … (args<sub>n-1</sub>) = { '''def''' g (args<sub>n</sub>) = E ; g }
</code>
 
где <tt>g</tt> — свежий идентификатор. Или, если короче, с использованием анонимной фунции:
 
<code>
'''def''' f (args<sub>1</sub>) … (args<sub>n-1</sub>) = ( args<sub>n</sub> ) => E
</code>
 
Выполнение этого шага ''n'' раз приводит к тому, что
 
<code>
'''def''' f (args<sub>1</sub>) … (args<sub>n</sub>) = E
</code>
 
эквивалентно
 
<code>
'''def''' f = (args<sub>1</sub>) => … => (args<sub>n</sub>) => E
</code>
 
Или, эквивалентно, с другим определением:
 
<code>
'''val''' f = (args<sub>1</sub>) => … => (args<sub>n</sub>) => E
</code>
 
Такой стиль определения и применения функций называется ''каррирование'' (currying) в честь своего популяризатора Хаскелла Карри, логика XX века, хотя идея принадлежит Мозесу Шонфинкелю и Готтлобу Фреге.
 
Тип функции, возвращающей функцию, выражается аналогично списку ее параметров. Возьмем к примеру последнюю формулировку <tt>sum</tt>. Тип <tt>sum</tt> — <tt>(Int => Int) => (Int, Int) => Int</tt>. Это возможно, потому что типы функций правоассоциативны. То есть, <tt>T1 => T2 => T3</tt> эквивалентно <tt>T1 => (T2 => T3)</tt>.
 
'''Упражнение 5.2.1''' 1. Функция <tt>sum</tt> использует линейную рекурсию. Напишите хвостоворекурсивный вариант, заполнив ??.
 
<font size=3><syntaxhighlight lang=Scala>
def sum(f: Int => Int)(a: Int, b: Int): Int = {
def iter(a: Int, result: Int): Int = {
if (??) ??
else iter(??, ??)
}
iter(??, ??)
}
</syntaxhighlight></font>
 
'''Упражнение 5.2.2''' Напишите функцию <tt>product</tt>, которая вычисляет произведение значений функций в точках данного интервала.
 
'''Упражнение 5.2.3''' Напишите <tt>factorial</tt> в терминах <tt>product</tt>.
 
'''Упражнение 5.2.4''' Можете ли вы написать более общую функцию, которая обобщает <tt>sum</tt> и <tt>product</tt>?
 
== Пример: поиск неподвижных точек функций ==