写一个lambda表达式的“curried版本”

时间:2013-04-15 21:33:25

标签: haskell lambda currying

我正在研究Haskell,并试图理解如何将currying概念应用于函数。我理解currying本质上是一种带有多个参数的函数并将函数应用于一个参数的方法,返回一个应用于第二个函数的函数,依此类推......而不会失去任何表达性。我正在研究的一个教程问:

"写一个2 * (\x y -> x*y)2 3"的咖喱版本

我希望有人可以帮我告诉我如何解决这个问题。提前致谢

编辑:回应两位评论者,我可以看到识别

(\x y -> x*y) :: Num a => a -> a -> a

...是我的第一步。在功能性编程方面我的学习曲线相当缓慢(也是新的SO海报,所以我打破了任何礼仪的借口)......我的下一步是什么?

编辑2:@Mikhail,我看到uncurry应用于lambda表达式的类型会形成某种形式(给定uncurry :: (a -> b -> c) -> (a,b) -> c

Num a => (a,a) -> a 

1 个答案:

答案 0 :(得分:5)

您对currying的基本理解是正确的。具体来说,它是关于转换一个将其参数作为元组的函数,例如

add :: (Int, Int) -> Int
add    (x, y)     =  x + y

进入一个一次获取一个参数的函数:

add' :: Int -> Int -> Int
add'    x      y   =  x + y

此方案允许您将现有的函数置于部分应用程序,即将其应用于某些但不是所有参数。例如,我们可以

succ :: Int -> Int
succ =  add' 1

我们将add'应用于它的第一个参数并产生一个仍然需要剩余参数的函数。

逆转换称为uncurrying,并将一个将其参数“逐个”的函数转换为一个函数,该函数将其参数“all once”作为元组。

两个转换都可以由高阶函数族捕获。也就是说,对于二进制函数,有

curry :: ((a, b) -> c) -> (a -> b -> c)
curry    f             =  \x y -> f (x, y)

uncurry :: (a -> b -> c) -> ((a, b) -> c)
uncurry    f             =  \(x, y) -> f x y

对于三元函数,有

curry3 :: ((a, b, c) -> d) -> (a -> b -> c -> d)
curry3    f                =  \x y z -> f (x, y, z)

uncurry3 :: (a -> b -> c -> d) -> ((a, b, c) -> d)
uncurry3    f                  =  \(x, y, z) -> f x y z

等等。

现在让我们来看看你的例子:

2 * (\x y -> x * y) 2 3

在这里,您将文字2乘以函数(\x y -> x * y)的应用结果,该函数将其两个参数xy相乘。正如您所看到的,此函数已经“逐个”获取其参数。因此,它已经咖喱了。所以,如果他们要求写一个这个表达式的咖喱版本,那么你的教程意味着什么呢?我们可以做的是通过使乘法函数将“一次性全部”变为参数来编写一个 uncurried 版本:(\(x, y) -> x * y)。然后我们得到

2 * (\(x, y) -> x * y) (2, 3)

现在请注意,可以将(\(x, y) -> x * y)写为uncurry (*),这会给我们

2 * uncurry (*) (2, 3)

如果我们也发现(*)的第一个申请(或实际申请,复数;-)),我们会产生

uncurry (*) (2, uncurry (*) (2, 3))

我怀疑这是否是你的教程中练习背后的意图,但我希望这能为你提供一些关于讨论和解决问题的见解。