use*_*112 8 haskell functional-programming currying
我不认为我完全理解currying,因为我无法看到它可以提供任何巨大的好处.也许有人可以用一个例子来启发我,说明它为何如此有用.它真的有好处和应用,还是仅仅是一个过度赞赏的概念?
phi*_*ler 12
(currying和部分应用之间略有不同,虽然它们密切相关;因为它们经常混合在一起,我会处理这两个术语.)
我首先意识到好处的地方是我看到切片操作员:
incElems = map (+1)
--non-curried equivalent: incElems = (\elems -> map (\i -> (+) 1 i) elems)
Run Code Online (Sandbox Code Playgroud)
IMO,这很容易阅读.现在,如果类型(+)是(Int,Int) -> Int*,这是未经证实的版本,它会(反直觉地)导致错误 - 但是curryied,它按预期工作,并且具有类型[Int] -> [Int].
你在评论中提到了C#lambdas.在C#中,你可以这样写incElems,给定一个函数plus:
var incElems = xs => xs.Select(x => plus(1,x))
Run Code Online (Sandbox Code Playgroud)
如果你习惯了无点样式,你会发现x这里是多余的.逻辑上,该代码可以减少到
var incElems = xs => xs.Select(curry(plus)(1))
Run Code Online (Sandbox Code Playgroud)
由于缺少使用C#lambdas的自动部分应用程序,这很糟糕.这是确定currying实际有用的关键点:主要是当它发生隐式时.对我来说,map (+1)最容易阅读,然后来,如果没有真正的理由,可能应该避免.Select(x => plus(1,x))版本curry.
现在,如果可读,那么好处总结为更短,更易读和更简洁的代码 - 除非有一些滥用无点风格的做法(我很喜欢(.).(.),但它是......特别的)
而且,如果不使用curried函数,lambda演算将变得不可能,因为它只有一个值(但因此更高阶)函数.
*当然它实际上在Num,但它目前更像这样的可读性.
更新:currying实际上是如何工作的.
看看plusC#中的类型:
int plus(int a, int b) {..}
Run Code Online (Sandbox Code Playgroud)
你必须给它一个价值元组 - 不是用C#术语,而是数学上说的; 你不能只留下第二个价值.在哈斯克尔术语中,就是这样
plus :: (Int,Int) -> Int,
Run Code Online (Sandbox Code Playgroud)
可以像
incElem = map (\x -> plus (1, x)) -- equal to .Select (x => plus (1, x))
Run Code Online (Sandbox Code Playgroud)
那种类型的字符太多了.假设您希望将来更频繁地这样做.这是一个小帮手:
curry f = \x -> (\y -> f (x,y))
plus' = curry plus
Run Code Online (Sandbox Code Playgroud)
这使
incElem = map (plus' 1)
Run Code Online (Sandbox Code Playgroud)
让我们将它应用于一个具体的值.
incElem [1]
= (map (plus' 1)) [1]
= [plus' 1 1]
= [(curry plus) 1 1]
= [(\x -> (\y -> plus (x,y))) 1 1]
= [plus (1,1)]
= [2]
Run Code Online (Sandbox Code Playgroud)
在这里你可以看到curry工作.它将标准的haskell样式函数application(plus' 1 1)转换为对"tupled"函数的调用 - 或者,在更高级别上查看,将"tupled"转换为"untupled"版本.
幸运的是,大多数情况下,您不必担心这一点,因为有自动部分应用程序.
这不是自切片面包以来最好的东西,但如果你无论如何使用lambdas,在不使用lambda语法的情况下使用高阶函数会更容易.相比:
map (max 4) [0,6,9,3] --[4,6,9,4]
map (\i -> max 4 i) [0,6,9,3] --[4,6,9,4]
Run Code Online (Sandbox Code Playgroud)
当你使用函数式编程时,这些类型的构造经常出现,它是一个很好的快捷方式,让你从稍高的层次思考问题 - 你映射的是" max 4"函数,而不是一些随机的恰好定义为的函数(\i -> max 4 i).它可以让您更轻松地开始更高级别的间接思考:
let numOr4 = map $ max 4
let numOr4' = (\xs -> map (\i -> max 4 i) xs)
numOr4 [0,6,9,3] --ends up being [4,6,9,4] either way;
--which do you think is easier to understand?
Run Code Online (Sandbox Code Playgroud)
那就是说,它不是灵丹妙药; 有时你的函数的参数对于你尝试用currying做的事情是错误的顺序,所以你无论如何都要求助于lambda.然而,一旦你习惯了这种风格,你就会开始学习如何设计你的功能以便与它一起工作,一旦这些神经元开始连接你的大脑,以前复杂的结构可以开始显得比较明显.