- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Изучай Haskell во имя добра! - Миран Липовача
Шрифт:
Интервал:
Закладка:
Посмотрим на эту простую функцию:
multThree :: Int -> Int -> Int -> Int
multThree x y z = x * y * z
Что происходит, если мы вызываем multThree 3 5 9 или ((multThree 3) 5) 9? Сначала значение 3 применяется к multThree, так как они разделены пробелом. Это создаёт функцию, которая принимает один параметр и возвращает новую функцию, умножающую на 3. Затем значение 5 применяется к новой функции, что даёт функцию, которая примет параметр и умножит его уже на 15. Значение 9 применяется к этой функции, и получается результат 135. Вы можете думать о функциях как о маленьких фабриках, которые берут какие-то материалы и что-то производят. Пользуясь такой аналогией, мы даём фабрике multThree число 3, и, вместо того чтобы выдать число, она возвращает нам фабрику немного поменьше. Эта новая фабрика получает число 5 и тоже выдаёт фабрику. Третья фабрика при получении числа 9 производит, наконец, результат — число 135. Вспомним, что тип этой функции может быть записан так:
multThree :: Int -> (Int -> (Int -> Int))
Перед символом –> пишется тип параметра функции; после записывается тип значения, которое функция вернёт. Таким образом, наша функция принимает параметр типа Int и возвращает функцию типа Int -> (Int –> Int). Аналогичным образом эта новая функция принимает параметр типа Int и возвращает функцию типа Int -> Int. Наконец, функция принимает параметр типа Int и возвращает значение того же типа Int.
Рассмотрим пример создания новой функции путём вызова функции с недостаточным числом параметров:
ghci> let multTwoWithNine = multThree 9
ghci> multTwoWithNine 2 3
54
В этом примере выражение multThree 9 возвращает функцию, принимающую два параметра. Мы называем эту функцию multTwoWithNine. Если при её вызове предоставить оба необходимых параметра, то она перемножит их между собой, а затем умножит произведение на 9.
Вызывая функции не со всеми параметрами, мы создаём новые функции «на лету». Допустим, нужно создать функцию, которая принимает число и сравнивает его с константой 100. Можно сделать это так:
compareWithHundred :: Int -> Ordering
compareWithHundred x = compare 100 x
Если мы вызовем функцию с 99, она вернёт значение GT. Довольно просто. Обратите внимание, что параметр x находится с правой стороны в обеих частях определения. Теперь подумаем, что вернёт выражение compare 100. Этот вызов вернёт функцию, которая принимает параметр и сравнивает его с константой 100. Ага-а! Не этого ли мы хотели? Можно переписать функцию следующим образом:
compareWithHundred :: Int -> Ordering
compareWithHundred = compare 100
Объявление типа не изменилось, так как выражение compare 100 возвращает функцию. Функция compare имеет тип (Ord a) => a –> (a –> Ordering). Когда мы применим её к 100, то получим функцию, принимающую целое число и возвращающую значение типа Ordering.
Сечения
Инфиксные функции могут быть частично применены при помощи так называемых сечений. Для построения сечения инфиксной функции достаточно поместить её в круглые скобки и предоставить параметр только с одной стороны. Это создаст функцию, которая принимает один параметр и применяет его к стороне с пропущенным операндом. Вот донельзя простой пример:
divideByTen :: (Floating a) => a –> a
divideByTen = (/10)
Вызов, скажем, divideByTen 200 эквивалентен вызову 200 / 10, равно как и (/10) 200:
ghci> divideByTen 200
20.0
ghci> 200 / 10
20.0
ghci> (/10) 200
20.0
А вот функция, которая проверяет, находится ли переданный символ в верхнем регистре:
isUpperAlphanum :: Char –> Bool
isUpperAlphanum = (`elem` ['А'..'Я'])
Единственная особенность при использовании сечений – применение знака «минус». По определению сечений, (–4) – это функция, которая вычитает четыре из переданного числа. В то же время для удобства (–4) означает «минус четыре». Если вы хотите создать функцию, которая вычитает четыре из своего аргумента, выполняйте частичное применение таким образом: (subtract 4).
Печать функций
До сих пор мы давали частично применённым функциям имена, после чего добавляли недостающие параметры, чтобы всё-таки посмотреть на результаты. Однако мы ни разу не попробовали напечатать сами функции. Попробуем? Что произойдёт, если мы попробуем выполнить multThree 3 4 в GHCi вместо привязки к имени с помощью ключевого слова let либо передачи другой функции?
ghci> multThree 3 4
<interactive>:1:0:
No instance for (Show (a –> a))
arising from a use of `print' at <interactive>:1:0–12
Possible fix: add an instance declaration for (Show (a –> a))
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
Теперь попробуем применить ФВП для реализации очень полезной функции из стандартной библиотеки. Она называется zipWith. Эта функция принимает функцию и два списка, а затем соединяет списки, применяя переданную функцию для соответствующих элементов. Вот как мы её реализуем:
zipWith' :: (a –> b –> c) –> [a] –> [b] –> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
Посмотрите на объявление типа. Первый параметр – это функция, которая принимает два значения и возвращает одно. Параметры этой функции не обязательно должны быть одинакового типа, но могут. Второй и третий параметры – списки. Результат тоже является списком. Первым идёт список элементов типа a, потому что функция сцепления принимает значение типа a в качестве первого параметра. Второй должен быть списком из элементов типа b, потому что второй параметр у связывающей функции имеет тип b. Результат – список элементов типа c. Если объявление функции говорит, что она принимает функцию типа a –> b –> c как параметр, это означает, что она также примет и функцию a –> a –> a, но не наоборот.

