咖喱的好处是什么? [英] What are the benefits of currying?

查看:109
本文介绍了咖喱的好处是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不认为我很了解咖喱,因为我无法看到它可以提供的任何巨大好处。也许有人可以用一个例子来说明我为什么这么有用。它是真的有益处还是应用程序,还是它只是一个过度赞赏的概念?

解决方案

部分应用,尽管它们密切相关;因为它们经常混合在一起,所以我会处理这两个术语。)



我首先意识到收益的地方是当我看到切片运算符时:

  incElems = map (+1)
- 非咖喱等价物:incElems =(\elems - > map(\ i - >(+)1 i)elems)

IMO,这是完全容易阅读的。现在,如果(+)的类型是(Int,Int) - > Int *,这是不安全的版本,它会(与直觉相反)导致错误 - 但是curryied,它按预期工作,并且类型为 [Int] - > [Int]



您在评论中提到了C#lambda。在C#中,您可以像 incElems 这样写加:

  var incElems = xs => xs.Select(x => plus(1,x))

如果您习惯无点式,你会发现这里的 x 是多余的。从逻辑上讲,该代码可以缩减为

  var incElems = xs => xs.Select(咖喱(加)(1))

这是由于缺乏自动用C#lambdas部分应用。这是决定currying实际上有用的关键点:大多数时候它隐式地发生。对我来说, map(+1)是最容易阅读的,然后是 .Select(x => plus(1,x)),如果没有真正的理由,应该避免使用 curry 的版本。



现在,如果可读性好,那么它的优点总结为更短,更易读,更简洁的代码 - 除非有一些滥用无点风格的做法, c $ c>(。)。(。),但它是...特殊的)

另外,lambda微积分在没有使用curried函数,因为它只有一个值(但是因此为高阶)函数。



*当然它实际上在 Num ,但目前它的可读性更高。 :如何实现currying实际工作。



在C#中查看加上的类型:

  int plus(int a,int b){..} 

你必须给它一个元组的值s - 不是用C#术语,而是用数学方法说话;你不能忽略第二个价值。在haskell中,这是

  plus ::(Int,Int) - > Int,

可以像

<$ (1,x)) - 等于.Select(x => plus(1,x))
<= c $ c> incElem = map / code>

输入的字符太多了。假设你将来希望更频繁地这样做。这里有一个小帮手:

 咖喱f = \ x  - > (\y-> f(x,y))
加'=咖喱加

,它给出了

  incElem = map(加上'1)

让我们将其应用于一个具体值。

  incElem [1 ] 
=(map(plus'1))[1]
= [plus'1 1]
= [(curry plus)1 1]
= [(\ ($(x,y)))1 1]
= [plus(1,1)]
= [2]

您可以在工作中看到 curry 。它将一个标准的haskell样式函数应用程序(加上'1 1 )转换为对tupled函数的调用 - 或者从更高级别查看,将tupled 进入未添加版本。



幸运的是,大多数时候,您不必担心这一点,因为有自动部分应用程序。


I don't think I quite understand currying, since I'm unable to see any massive benefit it could provide. Perhaps someone could enlighten me with an example demonstrating why it is so useful. Does it truly have benefits and applications, or is it just an over-appreciated concept?

解决方案

(There is a slight difference between currying and partial application, although they're closely related; since they're often mixed together, I'll deal with both terms.)

The place where I realized the benefits first was when I saw sliced operators:

incElems = map (+1)
--non-curried equivalent: incElems = (\elems -> map (\i -> (+) 1 i) elems)

IMO, this is totally easy to read. Now, if the type of (+) was (Int,Int) -> Int *, which is the uncurried version, it would (counter-intuitively) result in an error -- but curryied, it works as expected, and has type [Int] -> [Int].

You mentioned C# lambdas in a comment. In C#, you could have written incElems like so, given a function plus:

var incElems = xs => xs.Select(x => plus(1,x))

If you're used to point-free style, you'll see that the x here is redundant. Logically, that code could be reduced to

var incElems = xs => xs.Select(curry(plus)(1))

which is awful due to the lack of automatic partial application with C# lambdas. And that's the crucial point to decide where currying is actually useful: mostly when it happens implicitly. For me, map (+1) is the easiest to read, then comes .Select(x => plus(1,x)), and the version with curry should probably be avoided, if there is no really good reason.

Now, if readable, the benefits sum up to shorter, more readable and less cluttered code -- unless there is some abuse of point-free style done is with it (I do love (.).(.), but it is... special)

Also, lambda calculus would get impossible without using curried functions, since it has only one-valued (but therefor higher-order) functions.

* Of course it actually in Num, but it's more readable like this for the moment.


Update: how currying actually works.

Look at the type of plus in C#:

int plus(int a, int b) {..}

You have to give it a tuple of values -- not in C# terms, but mathematically spoken; you can't just leave out the second value. In haskell terms, that's

plus :: (Int,Int) -> Int, 

which could be used like

incElem = map (\x -> plus (1, x)) -- equal to .Select (x => plus (1, x))

That's way too much characters to type. Suppose you'd want to do this more often in the future. Here's a little helper:

curry f = \x -> (\y -> f (x,y))
plus' = curry plus

which gives

incElem = map (plus' 1)

Let's apply this to a concrete value.

incElem [1] 
= (map (plus' 1)) [1]
= [plus' 1 1]
= [(curry plus) 1 1]
= [(\x -> (\y -> plus (x,y))) 1 1]
= [plus (1,1)]
= [2]

Here you can see curry at work. It turns a standard haskell style function application (plus' 1 1) into a call to a "tupled" function -- or, viewed at a higher level, transforms the "tupled" into the "untupled" version.

Fortunately, most of the time, you don't have to worry about this, as there is automatic partial application.

这篇关于咖喱的好处是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆