2016-10-22 2 views
1

При чтении this article я нашел пример формирования последовательности Фибоначчи с unfoldr функции:Как функция лямбда в unoldr может использоваться с двумя аргументами в Haskell?

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

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

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

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

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

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

+1

Подсказка: замените 'b' на' (b, c) 'в сигнатуре типа' unfoldr' и посмотрим, что произойдет. – duplode

ответ

6

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

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

В то время как мы можем, конечно, интерпретировать его в наших головах как функция двух аргументов, насколько это касается Haskell он принимает только один аргумент ...

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

... a пара типа (t, t) для некоторых t в категории Num. Все, что происходит в вашем примере, то, является то, что b в подписи unfoldr «s заменяется 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)) 

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

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

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


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

-1

Во-первых, unforldr aplied к FUNC ((а, б) -> Просто (а, (Ь, а + б)))

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

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

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

Смежные вопросы