Изучай Haskell во имя добра! (Липовача) - страница 50

– разница в том, что она не создаёт пару >(x, y), а возвращает >f x y. Одна функция высшего порядка может использоваться для решения множества задач, если она достаточно общая. Покажем на небольшом примере, что умеет наша функция >zipWith':

>ghci> zipWith' (+) [4,2,5,6] [2,6,2,3]

>[6,8,7,9]

>ghci> zipWith' max [6,3,2,1] [7,3,1,5]

>[7,3,2,5]

>ghci> zipWith' (++) ["шелдон ", "леонард "] ["купер", "хофстадтер"]

>["шелдон купер","леонард хофстадтер"]

>ghci> zipWith' (*) (replicate 5 2) [1..]

>[2,4,6,8,10]

>ghci> zipWith' (zipWith' (*)) [[1,2,3],[3,5,6],[2,3,4]] [[3,2,2],[3,4,5],[5,4,3]] [[3,4,6],[9,20,30],[10,12,12]]

Как видите, одна-единственная функция высшего порядка может применяться самыми разными способами.

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

Теперь реализуем ещё одну функцию из стандартной библиотеки, >flip. Функция >flip принимает функцию и возвращает функцию. Единственное отличие результирующей функции от исходной – первые два параметра переставлены местами. Мы можем реализовать >flip следующим образом:

>flip' :: (a –> b –> c) –> (b –> a –> c)

>flip' f = g

>  where g x y = f y x

Читая декларацию типа, мы видим, что функция принимает на вход функцию с параметрами типов >a и >b и возвращает функцию с параметрами >b и >a. Так как все функции на самом деле каррированы, вторая пара скобок не нужна, поскольку символ >–> правоассоциативен. Тип >(a –> b –> c) –> (b –> a –> c) – то же самое, что и тип >(a –> b –> c) –> (b –> (a –> c)), а он, в свою очередь, представляет то же самое, что и тип >(a –> b –> c) –> b –> a –> c. Мы записали, что >g x y = f y x. Если это верно, то верно и следующее: >f y x = g x y. Держите это в уме – мы можем реализовать функцию ещё проще.

>flip' :: (a –> b –> c) –> b –> a –> c

>flip' f y x = f x y

Здесь мы воспользовались тем, что функции каррированы. Когда мы вызываем функцию >flip' f без параметров >y и >x, то получаем функцию, которая принимает два параметра, но переставляет их при вызове. Даже несмотря на то, что такие «перевёрнутые» функции обычно передаются в другие функции, мы можем воспользоваться преимуществами каррирования при создании ФВП, если подумаем наперёд и запишем, каков будет конечный результат при вызове полностью определённых функций.

>ghci> zip [1,2,3,4,5,6] "привет"

>[(1,'п'),(2,'р'),(3,'и'),(4,'в'),(5,'е'),(6,'т')]

>ghci> flip' zip [1,2,3,4,5] "привет"

>[('п',1),('р',2),('и',3),('в',4),('е',5),('т',6)]

>ghci> zipWith div [2,2..] [10,8,6,4,2]

>[0,0,0,0,1]

>ghci> zipWith (flip' div) [2,2..] [10,8,6,4,2]

>[5,4,3,2,1]

Если применить функцию >flip' к >zip, то мы получим функцию, похожую на >zip