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

How lambda function in unfoldr can be used with two arguments in Haskell?

在阅读 this article 时,我找到了使用 unfoldr 函数生成斐波那契数列的示例:

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

但是当我查看 documentation 时,我发现 unfoldr 函数中的 lambda 只接受一个参数 b:

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

文档中的示例也仅演示了一种参数用法:

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

所以,我很感兴趣如何将它应用于斐波那契示例中的两个参数?

首先,unforldr 应用于 func ((a,b) -> Just (a, (b, a+b)))

returns 函数 b -> [a]。这是柯里化。

然后将结果函数应用于第二个参数(0, 1)

这就结束了最终的结果。

这是你的功能:

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

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

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

...对于 Num class 中的某些 t 类型 (t, t)。那么,在您的示例中发生的所有事情就是 unfoldr 签名中的 bNum t => (t, t) 替换,aNum 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))

事实上,这种转换——用行话来说叫做currying——总是是可能的,甚至还有一个函数因为这样做...

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接受一个参数(即我们的第一个参数),returns另一个函数也接受一个参数(即our 第二个参数).


那么,主要的收获是 所有 Haskell 函数只接受一个参数 。幸运的是,很容易编写像接受两个参数一样工作的函数。通常情况下,这是通过以柯里化的方式编写函数来完成的(也就是说,像 foo2 这样的函数 returns 另一个函数),但有时传递多个值是必要的或方便的成对,就像您在示例中必须做的那样。