5

在 Haskell 中编写一些 lambda 函数时,我最初编写的函数如下:

tru = \t f -> t
fls = \t f -> f

然而,我很快从网上的例子中注意到,这些函数经常写成这样:

tru = \t -> \f -> t
fls = \t -> \f -> f

具体来说,传递给函数的每个项目都有自己的\->与上面相反。在检查这些类型时,它们似乎是相同的。我的问题是,它们是等效的还是实际上在某些方面有所不同?不仅对这两个功能,而且对一般功能有影响吗?非常感谢!

4

2 回答 2

14

它们是相同的,Haskell 会自动事物进行 curry 以保持语法良好。以下都是等价的**

foo a b = (a, b)
foo a = \b -> (a, b)
foo = \a b -> (a, b)
foo = \a -> \b -> (a, b)
-- Or we can simply eta convert leaving
foo = (,)

如果您想成为惯用语,请选择第一个或最后一个。引入不必要的 lambda 对教授柯里化很有好处,但在实际代码中只会增加语法混乱。

然而,在原始 lambda 演算(不是 Haskell)中,大多数手动使用

\a -> \b -> a b

因为人们不会手工编写很多 lambda 演算,而且当他们这样做时,他们倾向于坚持不加糖的 lambda 演算以使事情变得简单。

** 以单态限制为模,无论如何它都不会影响您的类型签名。

于 2013-10-06T04:56:20.633 回答
6

虽然,正如 jozefg 所说,它们本身是等价的,但当与局部变量绑定结合使用时,它们可能会导致不同的执行行为。考虑

f, f' :: Int -> Int -> Int

有两个定义

f a x = μ*x
 where μ = sum [1..a]

f' a = \x -> μ*x
 where μ = sum [1..a]

这些肯定看起来是等价的,并且肯定会产生相同的结果。

GHCi,版本 7.6.2:http ://www.haskell.org/ghc/ :? 寻求帮助
...
[1 of 1] 编译 Main (def0.hs, 解释)
好的,加载的模块:Main。
*Main> sum $ map (f 10000) [1..10000]
2500500025000000
*Main> sum $ map (f' 10000) [1..10000]
2500500025000000

但是,如果您自己尝试此操作,您会发现 withf会花费大量时间,而 withf'您会立即获得结果。原因是它f'是以提示 GHC 编译它的形式编写的,以便f' 10000在开始将它映射到列表之前进行实际评估。在该步骤中,该值μ被计算并存储在 的闭包中(f' 10000)。另一方面,f被简单地视为“两个变量的一个函数”;(f 10000)仅存储为包含参数的闭包,10000并且一μ开始根本不计算。仅当map应用于(f 10000)列表中的每个元素时,才会计算整体,这对于 中的每个元素都sum [1..a]需要一些时间。和[1..10000]f',这不是必需的,因为μ是预先计算的。

原则上,公共子表达式消除是 GHC 能够自行完成的优化,因此即使使用f. 但你不能指望它。

于 2013-10-06T13:24:32.397 回答