tru = \t f -> t
fls = \t f -> f
然而,我很快就从在线示例中注意到这样的函数经常被编写成以下形式:
tru = \t -> \f -> t
fls = \t -> \f -> f
具体来说,传递给该函数的每个项目都有自己的\
和->
,与上面的情况不同。当检查它们的类型时,它们似乎是相同的。我的问题是,它们是否等效或实际上存在某些差异?并且不仅对于这两个函数,对于一般的函数来说,这是否有所区别?非常感谢!
tru = \t f -> t
fls = \t f -> f
tru = \t -> \f -> t
fls = \t -> \f -> f
具体来说,传递给该函数的每个项目都有自己的\
和->
,与上面的情况不同。当检查它们的类型时,它们似乎是相同的。我的问题是,它们是否等效或实际上存在某些差异?并且不仅对于这两个函数,对于一般的函数来说,这是否有所区别?非常感谢!
它们是相同的,Haskell会自动柯里化来保持语法的美妙统一。以下所有的表达都是等价的。
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
因为人们不会手写太多的λ演算,而且当他们这样做时,他们倾向于使用未加糖的λ演算来保持简单。
** 模掉单态限制(monomorphism restriction),但对于带类型签名的情况下也不会影响你。
虽然,如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] Compiling Main ( def0.hs, interpreted )
Ok,模块已加载:Main。
*Main> sum $ map (f 10000) [1..10000]
2500500025000000
*Main> sum $ map (f' 10000) [1..10000]
2500500025000000
然而,如果你自己尝试一下,你会发现使用f
需要花费很长时间,而使用f'
则能立即得到结果。原因是f'
的形式促使GHC编译它,以便实际上在将其映射到列表之前评估f' 10000
。在这一步中,计算并存储了值μ
在(f' 10000)
的闭包中。另一方面,f
被简单地视为“两个变量的一个函数”;(f 10000)
仅被存储为包含参数10000
的闭包,而μ
根本没有被计算。只有当map
将(f 10000)
应用于列表中的每个元素时,才会计算整个sum [1..a]
,这需要一些时间对于[1..10000]
中的每个元素。使用f'
,这是不必要的,因为μ
已经预先计算了。
原则上,公共子表达式消除是GHC能够自行完成的优化,因此你可能会在像f
这样的定义中获得良好的性能。但是你不能真正依赖它。
f . g = \x -> f (g x)
。 - kqr(a,b) -> c
转换为a -> (b -> c)
(因此curry :: ((a,b) -> c) -> a -> b -> c
)。确实,这种符号表示法旨在简化编写柯里化函数。(这可能是您想要表达的,但我认为值得明确说明一下。) - Antal Spector-Zabusky