我读了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个响应之后,我了解到的好处是,在某些情况下,当我们从库里创建一个新函数时,某些参数不会被重新评估。
我在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的2次连续调用中,值100的ToString()评估被调用了两次(每次调用一次),所以我在这里看不到评估的任何 yield 。我想念什么吗?
最佳答案
首先考虑fn(x,y,z)比较容易。可以通过使用fn(x,y)来实现这一点,从而为您提供仅包含一个参数z的函数。只需使用x和y进行的所有操作都可以由返回函数保留的闭包来完成和存储。
现在,您可以使用不同的z值多次调用返回的函数,而不必重新计算所需的x和y。
编辑:
有效地 curry 有两个原因。
参数减少
正如Cameron所说,将带有2个参数的函数转换为仅带1个参数的函数。使用参数调用此curried函数的结果与使用2个参数调用原始函数的结果相同。
由于C#中存在Lambda,因此其值(value)有限,因为它们仍然可以提供这种效果。尽管您使用的是C#2,但问题中的Curry函数具有更大的值(value)。
分期计算
curry 的另一个原因是我前面所说的。当最终参数提供给curried函数时,允许分阶段进行复杂/昂贵的操作并重复使用几次。
在C#中,这种类型的计数并不是真正可能的,它确实需要一种可以自然地实现其任何功能的函数式语言。
结论
您提到的通过Curry进行的参数缩减在C#2中很有用,但由于Lambda,在C#3中已大大降低了值(value)。
关于C#Lambda- curry 用例,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/520083/