Как и обычные функции, лямбда-выражения могут принимать произвольное количество параметров:
>ghci> zipWith (\a b –> (a * 30 + 3) / b) [5,4,3,2,1] [1,2,3,4,5]
>[153.0,61.5,31.0,15.75,6.6]
По аналогии с обычными функциями, можно выполнять сопоставление с образцом в лямбда-выражениях. Единственное отличие в том, что нельзя определить несколько образцов для одного параметра – например, записать для одного параметра образцы >[]
и >(x: xs)
и рассчитывать, что выполнение перейдёт к образцу >(x:xs)
в случае неудачи с >[]
. Если сопоставление с образцом в анонимной функции заканчивается неудачей, происходит ошибка времени выполнения, так что поосторожнее с этим!
>ghci> map (\(a,b) –> a + b) [(1,2),(3,5),(6,3),(2,6),(2,5)]
>[3,8,9,8,7]
Обычно анонимные функции заключаются в круглые скобки, если только мы не хотим, чтобы лямбда-выражение заняло всю строку. Интересная деталь: поскольку все функции каррированы по умолчанию, допустимы две эквивалентные записи.
>addThree :: Int -> Int -> Int -> Int
>addThree x y z = x + y + z
>addThree' :: Int -> Int -> Int -> Int
>addThree' = \x -> \y -> \z -> x + y + z
Если мы объявим функцию подобным образом, то станет понятно, почему декларация типа функции представлена именно в таком виде. И в декларации типа, и в теле функции имеются три символа >–>
. Конечно же, первый способ объявления функций значительно легче читается; второй – это всего лишь очередная возможность продемонстрировать каррирование.
ПРИМЕЧАНИЕ. Обратите внимание на то, что во втором примере анонимные функции не заключены в скобки. Когда вы пишете анонимную функцию без скобок, предполагается, что вся часть после символов >–>
относится к этой функции. Так что пропуск скобок экономит на записи. Конечно, ничто не мешает использовать скобки, если это вам больше нравится.
Тем не менее есть случаи, когда использование такой нотации оправдано. Я думаю, что функция >flip
будет лучше читаться, если мы объявим её так:
>flip' :: (a –> b –> c) –> b –> a –> c
>flip' f = \x y –> f y x
Несмотря на то что эта запись равнозначна >flip' f x y = f y x
, мы даём понять, что данная функция чаще всего используется для создания новых функций. Самый распространённый сценарий использования >flip
– вызов её с некоторой функцией и передача результирующей функции в >map
или >zipWith
:
>ghci> zipWith (flip (++)) ["люблю тебя", "любишь меня"] ["я ", "ты "]
>["я люблю тебя","ты любишь меня"]
>ghci> map (flip subtract 20) [1,2,3,4]
>[19,18,17,16]
Итак, используйте лямбда-выражения таким образом, когда хотите явно показать, что ваша функция должна быть частично применена и передана далее как параметр.
Я вас сверну!
Когда мы разбирались с рекурсией, то во всех функциях для работы со списками наблюдали одну и ту же картину. Базовым случаем, как правило, был пустой список. Мы пользовались образцом >(x:xs)
и затем делали что-либо с «головой» и «хвостом» списка. Как выясняется, это очень распространённый шаблон. Были придуманы несколько полезных функций для его инкапсуляции. Такие функции называются свёртками (folds). Свёртки позволяют свести структуру данных (например, список) к одному значению.