1

我正在研究 Haskell,并试图了解如何将柯里化的概念应用于函数。我理解柯里化本质上是一种采用具有多个参数的函数并将该函数应用于一个参数,返回一个应用于第二个参数的函数,依此类推......而不会失去任何表现力。我正在研究的一个教程要求:

“写一个咖喱版的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 
4

1 回答 1

5

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

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,它将一个“一个接一个”接受参数的函数转换为一个“一次”接受其参数作为元组的函数。

这两种转换都可以被高阶函数族捕获。也就是说,对于二元函数有

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. 正如你所看到的,这个函数已经“一个接一个”地接受它的参数。因此,它已经咖喱了。因此,如果他们要求编写此表达式的咖喱版本,您的教程中的含义超出了我的范围。我们可以做的是通过让乘法函数“一次性”接受它的参数来编写一个非柯里化(\(x, y) -> x * y)版本:然后我们得到

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

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

2 * uncurry (*) (2, 3)

如果我们也 uncurry 的第一个应用程序(或者实际上是应用程序,复数;-))(*),我们产生

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

我怀疑这是否是您教程中练习的意图,但我希望这能让您对 currying 和 uncurrying 有所了解。

于 2013-04-16T09:48:53.963 回答