×
Traktatov.net » Изучай Haskell во имя добра! » Читать онлайн
Страница 40 из 245 Настройки

>  In the expression: print it

>  In a 'do' expression: print it

GHCi сообщает нам, что выражение порождает функцию типа >a –> a, но он не знает, как вывести её на экран. Функции не имеют экземпляра класса >Show, так что мы не можем получить точное строковое представление функций. Когда мы вводим, скажем, >1 + 1 в терминале GHCi, он сначала вычисляет результат (>2), а затем вызывает функцию >show для >2, чтобы получить текстовое представление этого числа. Текстовое представление >2 – это строка >"2", которая и выводится на экран.

ПРИМЕЧАНИЕ. Удостоверьтесь в том, что вы поняли, как работает каррирование и частичное применение функций, поскольку эти понятия очень важны.

Немного о высоких материях

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

>applyTwice :: (a –> a) –> a –> a

>applyTwice f x = f (f x)



Прежде всего, обратите внимание на объявление типа. Раньше мы не нуждались в скобках, потому что символ >–> обладает правой ассоциативностью. Однако здесь скобки обязательны. Они показывают, что первый параметр – это функция, которая принимает параметр некоторого типа и возвращает результат того же типа. Второй параметр имеет тот же тип, что и аргумент функции – как и возвращаемый результат. Мы можем прочитать данное объявление в каррированном стиле, но, чтобы избежать головной боли, просто скажем, что функция принимает два параметра и возвращает результат. Первый параметр – это функция (она имеет тип >a –> a), второй параметр имеет тот же тип >a. Заметьте, что совершенно неважно, какому типу будет соответствовать типовая переменная >a>Int, >String или вообще чему угодно – но при этом все значения должны быть одного типа.

ПРИМЕЧАНИЕ. Отныне мы будем говорить, что функция принимает несколько параметров, вопреки тому что в действительности каждая функция принимает только один параметр и возвращает частично применённую функцию. Для простоты будем говорить, что >a –> a –> a принимает два параметра, хоть мы и знаем, что происходит «за кулисами».

Тело функции >applyTwice достаточно простое. Мы используем параметр >f как функцию, применяя её к параметру >x (для этого разделяем их пробелом), после чего передаём результат снова в функцию >f. Давайте поэкспериментируем с функцией:

>ghci> applyTwice (+3) 10

>16

>ghci> applyTwice (++ " ХА-ХА") "ЭЙ"

>"ЭЙ ХА-ХА ХА-ХА"

>ghci> applyTwice ("ХА-ХА " ++) "ЭЙ"

>"ХА-ХА ХА-ХА ЭЙ"

>ghci> applyTwice (multThree 2 2) 9

>144

>ghci> applyTwice (3:) [1]

>[3,3,1]

Красота и полезность частичного применения очевидны. Если наша функция требует передать ей функцию одного аргумента, мы можем частично применить функцию-параметр таким образом, чтобы оставался неопределённым всего один параметр, и затем передать её нашей функции. Например, функция >+ принимает два параметра; с помощью сечений мы можем частично применить её так, чтобы остался только один.

Реализация функции zipWith

Теперь попробуем применить ФВП для реализации очень полезной функции из стандартной библиотеки. Она называется