我读了This article,我发现它很有趣。
对于那些不想阅读整篇文章的人来说,总结一下。作者实现了一个名为Curry的高阶函数,如下所示(我在没有内部类的情况下进行了重构):
public static Func<T1, Func<T2, TResult>>
Curry<T1, T2, TResult>(this Func<T1, T2, TResult> fn)
{
Func<Func<T1, T2, TResult>, Func<T1, Func<T2, TResult>>> curry =
f => x => y => f(x, y);
return curry(fn);
}这给了我们这样的能力,像F(x,y)这样的表达式。
Func<int, int, int> add = (x, y) => x + y;并以F.Curry()(x)(y)方式调用;
这部分我理解了,我发现它很酷,很极客。我没能理解的是这种方法的实际用法。何时何地需要这项技术,能从中获得什么?
提前谢谢。
编辑:在最初的3个响应之后,我知道在某些情况下,当我们从create创建一个新函数时,一些参数不会被重新计算。我用C#做了这个小测试(请记住,我只对C#实现感兴趣,而不是一般的curry理论):
public static void Main(string[] args)
{
Func<Int, Int, string> concat = (a, b) => a.ToString() + b.ToString();
Func<Int, Func<Int, string>> concatCurry = concat.Curry();
Func<Int, string> curryConcatWith100 = (a) => concatCurry(100)(a);
Console.WriteLine(curryConcatWith100(509));
Console.WriteLine(curryConcatWith100(609));
}
public struct Int
{
public int Value {get; set;}
public override string ToString()
{
return Value.ToString();
}
public static implicit operator Int(int value)
{
return new Int { Value = value };
}
}在连续两次调用curryConcatWith100时,值为100的ToString()求值被调用了两次(每次调用一次),所以我在这里看不到求值的任何好处。我是不是漏掉了什么?
发布于 2009-02-06 12:24:04
首先考虑fn(x,y,z)比较容易。这可以通过使用fn(x,y)提供一个函数,该函数只接受一个参数z。任何需要单独使用x和y完成的操作都可以通过一个闭包来完成并存储,返回的函数持有该闭包。
现在,您使用不同的z值多次调用返回的函数,而不必重新计算所需的x和y部分。
编辑:
有效地说,有两个原因需要使用curry。
参数约简
正如卡梅隆所说,将一个接受2个参数的函数转换为只接受1个参数的函数。用一个参数调用这个curried函数的结果与用2个参数调用原始函数的结果相同。
对于C#中存在的Lambdas,它的价值是有限的,因为它们无论如何都可以提供这种效果。虽然您使用的是C# 2,但是您问题中的Curry函数具有更大的价值。
分段计算
另一个使用curry的原因是我之前说过的。当最后的参数被提供给curried函数时,允许复杂/昂贵的操作被分段和多次重用。
这种类型的currying在C#中是不可能的,它确实需要一种函数式语言来实现它的任何函数。
结论
您提到的通过库里减少参数在C# 2中很有用,但在C# 3中由于Lambdas而大大降低了价值。
发布于 2009-02-06 13:04:42
Currying用于将具有x参数的函数转换为具有y参数的函数,因此可以将其传递给需要具有y参数的函数的另一个函数。
例如,Enumerable.Select(this IEnumerable<T> source, Func<TSource, bool> selector)接受一个只有一个参数的函数。Math.Round(double, int)是一个有2个参数的函数。
您可以使用currying将Round函数“存储”为数据,然后将这个curried函数传递给Select,如下所示
Func<double, int, double> roundFunc = (n, p) => Math.Round(n, p);
Func<double, double> roundToTwoPlaces = roundFunc.Curry()(2);
var roundedResults = numberList.Select(roundToTwoPlaces);这里的问题是还有匿名委托,这使得currying变得多余。事实上,匿名委托是一种讨好的形式。
Func<double, double> roundToTwoPlaces = n => Math.Round(n, 2);
var roundedResults = numberList.Select(roundToTwoPlaces);或者甚至只是
var roundedResults = numberList.Select(n => Math.Round(n, 2));鉴于某些函数式语言的语法,Currying是一种解决特定问题的方法。有了匿名委托和lambda运算符,.NET中的语法就简单多了。
发布于 2009-02-06 12:27:12
在某种意义上,curring是一种实现自动局部应用的技术。
更正式地说,currying是一种将函数转换为只接受一个参数的函数的技术。
反过来,当被调用时,该函数返回另一个接受一个且仅接受一个参数的函数。。。依此类推,直到“原始”函数能够被执行。
从codingforums中的线程
我特别喜欢这篇page上的解释和篇幅。
https://stackoverflow.com/questions/520083
复制相似问题