unoldr中的lambda函数如何与Haskell中的两个参数一起使用?

Mai*_*r00 1 lambda haskell types functional-programming unfold

在阅读本文时,我发现了使用unfoldr函数生成Fibonacci序列的示例:

fibs = unfoldr (\(a,b) -> Just (a,(b,a+b))) (0,1)
Run Code Online (Sandbox Code Playgroud)

但是当我查看文档时,我发现unfoldr函数中的lambda 只有一个参数b:

unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
Run Code Online (Sandbox Code Playgroud)

doc中的示例也只显示了一个参数用法:

unfoldr (\b -> if b == 0 then Nothing else Just (b, b-1)) 10
Run Code Online (Sandbox Code Playgroud)

所以,我很感兴趣它如何应用于两个参数,如Fibonacci例子?

dup*_*ode 6

这是你的功能:

GHCi> let foo = \(a,b) -> Just(a,(b,a+b))
Run Code Online (Sandbox Code Playgroud)

虽然我们当然可以将它解释为两个参数的函数,但就Haskell而言,它只需要一个参数......

foo :: Num t => (t, t) -> Maybe (t, (t, t))
Run Code Online (Sandbox Code Playgroud)

...一类型的(t, t)一些tNum类.所有这一切在你的例子是怎么回事,那么,就是bunfoldr"签名被替换Num t => (t, t),并a通过Num t => t,导致:

Num t => ((t, t) -> Maybe (t, (t, t))) -> (t, t) -> [t]
Run Code Online (Sandbox Code Playgroud)

一些额外的证据:

GHCi> :t unfoldr (\(a,b) -> Just (a,(b,a+b)))
unfoldr (\(a,b) -> Just (a,(b,a+b))) :: Num a => (a, a) -> [a]
Run Code Online (Sandbox Code Playgroud)

回答你的直接问题,这里有点偏离.将两个参数传递给Haskell函数的另一种方法是将它们分开传递,而不是成对传递:

GHCi> let foo2 a b = Just(a,(b,a+b))
GHCi> :t foo2
foo2 :: Num t => t -> t -> Maybe (t, (t, t))
Run Code Online (Sandbox Code Playgroud)

显然,如果你还有foo,你甚至不需要重写实现:

GHCi> let foo2 a b = foo (a, b)
GHCi> :t foo2
foo2 :: Num t => t -> t -> Maybe (t, (t, t))
Run Code Online (Sandbox Code Playgroud)

事实上,这种转变 - 用行话称为currying - 总是可行的,甚至还有一个功能......

GHCi> let foo2 = curry foo
GHCi> :t foo2
foo2 :: Num t => t -> t -> Maybe (t, (t, t))
Run Code Online (Sandbox Code Playgroud)

......以及另一个向相反方向前进的人:

GHCi> :t uncurry foo2
uncurry foo2 :: Num t => (t, t) -> Maybe (t, (t, t))
Run Code Online (Sandbox Code Playgroud)

然而,在所有这些中可能令人惊讶的是,即使foo2看起来更像是两个参数foo的函数,它仍然是一个参数的函数!诀窍是像foo2...... 那样的签名

foo2 :: Num t => t -> t -> Maybe (t, (t, t))
Run Code Online (Sandbox Code Playgroud)

...有一些省略的可选括号.遗漏掩盖了函数箭头->是右关联的事实.如果我们添加它们,我们得到:

foo2 :: Num t => t -> (t -> Maybe (t, (t, t)))
Run Code Online (Sandbox Code Playgroud)

也就是说,foo2接受一个参数(即我们的第一个参数)并返回另一个也接受一个参数的函数(即我们的第二个参数).


那么,主要的问题是所有Haskell函数只需要一个参数.幸运的是,编写像两个参数一样工作的函数非常容易.通常情况下,这是通过以curry方式编写函数来完成的(也就是说,就像foo2返回另一个函数一样的函数),但有时需要或方便地传递一对中的多个值,就像你不得不这样做在你的例子中.