Как лямбда-функцию в unfoldr можно использовать с двумя аргументами в Haskell?

Читая эту статью, я нашел пример генерации последовательности Фибоначчи с unfoldr функция:

fibs = unfoldr (\(a,b) -> Just (a,(b,a+b))) (0,1)

Но когда я посмотрел на документацию, я обнаружил, что лямбда в unfoldr функция принимает только один аргумент b:

unfoldr :: (b -> Maybe (a, b)) -> b -> [a]

Примеры в документе также демонстрируют использование только одного аргумента:

unfoldr (\b -> if b == 0 then Nothing else Just (b, b-1)) 10

Итак, мне интересно, как это можно применить к двум аргументам, как в примере Фибоначчи?

2 ответа

Решение

Это ваша функция:

GHCi> let foo = \(a,b) -> Just(a,(b,a+b))

Хотя мы, безусловно, можем интерпретировать это в наших головах как функцию двух аргументов, для Хаскелла это займет всего один аргумент...

foo :: Num t => (t, t) -> Maybe (t, (t, t))

... пара типов (t, t) для некоторых t в Num учебный класс. Все, что происходит в вашем примере, это то, что b в unfoldrподпись заменяется Num t => (t, t), а также a от Num t => t, в результате чего:

Num t => ((t, t) -> Maybe (t, (t, t))) -> (t, t) -> [t]

Некоторые дополнительные доказательства этого:

GHCi> :t unfoldr (\(a,b) -> Just (a,(b,a+b)))
unfoldr (\(a,b) -> Just (a,(b,a+b))) :: Num a => (a, a) -> [a]

С ответом на ваш ближайший вопрос вот небольшое отступление. Другой способ передачи двух аргументов в функцию Haskell - передача их отдельно, в отличие от пары:

GHCi> let foo2 a b = Just(a,(b,a+b))
GHCi> :t foo2
foo2 :: Num t => t -> t -> Maybe (t, (t, t))

Очевидно, если у вас еще есть foo вокруг вас даже не нужно переписывать реализацию:

GHCi> let foo2 a b = foo (a, b)
GHCi> :t foo2
foo2 :: Num t => t -> t -> Maybe (t, (t, t))

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

GHCi> let foo2 = curry foo
GHCi> :t foo2
foo2 :: Num t => t -> t -> Maybe (t, (t, t))

... а также еще один для движения в противоположном направлении:

GHCi> :t uncurry foo2
uncurry foo2 :: Num t => (t, t) -> Maybe (t, (t, t))

Что может быть удивительным во всем этом, однако, как даже foo2что выглядит еще больше как функция двух аргументов, чем foo, все еще является функцией одного аргумента! Хитрость в том, что такая подпись foo2...

foo2 :: Num t => t -> t -> Maybe (t, (t, t))

... имеет некоторые опущенные, необязательные скобки. Упущение скрывает тот факт, что функция стрелка, ->является правоассоциативным. Если мы добавим их, мы получим:

foo2 :: Num t => t -> (t -> Maybe (t, (t, t)))

То есть, foo2 принимает один аргумент (то есть наш первый аргумент) и возвращает другую функцию, которая также принимает один аргумент (то есть наш второй аргумент).


Таким образом, основной вывод заключается в том, что все функции Haskell принимают только один аргумент. К счастью, очень легко написать функции, которые работают так, как будто они принимают два аргумента. Чаще всего это делается путем написания функции карри (т.е. как foo2 который возвращает другую функцию), но иногда бывает необходимо или удобно передавать несколько значений в паре, как это было в вашем примере.

Во-первых, unforldr применяется к func ((a,b) -> Just (a, (b, a+b)))

возвращает функцию b -> [a]. Это карри.

Затем функция результата применяется ко второму аргументу (0, 1).

который заканчивается конечным результатом.

Другие вопросы по тегам