C# lambda - 柯里化用例
我读了 这篇文章我觉得很有趣。
为那些不想阅读整篇文章的人总结一下。 作者实现了一个名为 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# 实现感兴趣,而不是一般的柯里理论):
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() 评估被调用两次(每次调用一次)所以我在这里没有看到任何评估收益。 我错过了什么吗?
I read This article and i found it interesting.
To sum it up for those who don't want to read the entire post. The author implements a higher order function named Curry like this (refactored by me without his internal class):
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);
}
That gives us the ability to take an expression like F(x, y)
eg.
Func<int, int, int> add = (x, y) => x + y;
and call it in the F.Curry()(x)(y) manner;
This part i understood and i find it cool in a geeky way. What i fail to wrap my head around is the practical usecases for this approach. When and where this technique is necessary and what can be gained from it?
Thanks in advance.
Edited:
After the initial 3 responses i understand that the gain would be that in some cases when we create a new function from the curried some parameters are not re evalued.
I made this little test in C# (keep in mind that i'm only interested in the C# implementation and not the curry theory in general):
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 };
}
}
On the 2 consecutive calls to curryConcatWith100 the ToString() evaluation for the value 100 is called twice (once for each call) so i dont see any gain in evaluation here. Am i missing something ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
柯里化用于将具有 x 参数的函数转换为具有 y 参数的函数,因此它可以传递给另一个需要具有 y 参数的函数的函数。
例如,
Enumerable.Select(this IEnumerablesource, Funcselector)
采用带有 1 个参数的函数。Math.Round(double, int)
是一个有 2 个参数的函数。您可以使用柯里化将
Round
函数“存储”为数据,然后将该柯里化函数传递给Select
,如下所示。这里的问题是,还有匿名委托,使柯里化变得多余。 事实上,匿名委托是柯里化的一种形式。
或者甚至只是
柯里化(Currying)是在给定某些函数式语言的语法的情况下解决特定问题的一种方法。 使用匿名委托和 lambda 运算符,.NET 中的语法要简单得多。
Currying is used to transform a function with x parameters to a function with y parameters, so it can be passed to another function that needs a function with y parameters.
For example,
Enumerable.Select(this IEnumerable<T> source, Func<TSource, bool> selector)
takes a function with 1 parameter.Math.Round(double, int)
is a function that has 2 parameters.You could use currying to "store" the
Round
function as data, and then pass that curried function to theSelect
like soThe problem here is that there's also anonymous delegates, which make currying redundant. In fact anonymous delegates are a form of currying.
Or even just
Currying was a way of solving a particular problem given the syntax of certain functional languages. With anonymous delegates and the lambda operator the syntax in .NET is alot simpler.
首先考虑 fn(x,y,z) 更容易。 这可以通过使用 fn(x,y) 进行柯里化,为您提供一个仅采用一个参数 z 的函数。 无论需要单独使用 x 和 y 完成什么操作,都可以通过返回函数所保存的闭包来完成和存储。
现在,您可以使用不同的 z 值多次调用返回的函数,而无需重新计算所需的 x 和 y 部分。
编辑:
柯里化实际上有两个原因。
参数减少
正如 Cameron 所说,将带有 2 个参数的函数转换为仅带有 1 个参数的函数。使用参数调用此柯里化函数的结果与使用 2 个参数调用原始函数的结果相同。
由于 C# 中存在 Lambda,因此其价值有限,因为它们无论如何都可以提供这种效果。 尽管您使用的是 C# 2,但您问题中的 Curry 函数具有更大的价值。
分段计算
柯里化的另一个原因如我之前所述。 当最终参数提供给柯里化函数时,允许多次执行复杂/昂贵的操作。
这种类型的柯里化在 C# 中实际上是不可能的,它确实需要一种可以本地柯里化其任何函数的函数式语言来实现。
结论
通过您提到的 Curry 进行参数减少在 C# 2 中很有用,但由于 Lambda 在 C# 3 中的价值大大降低。
Its easier to first consider fn(x,y,z). This could by curried using fn(x,y) giving you a function that only takes one parameter, the z. Whatever needs to be done with x and y alone can be done and stored by a closure that the returned function holds on to.
Now you call the returned function several times with various values for z without having to recompute the part the required x and y.
Edit:
There are effectively two reasons to curry.
Parameter reduction
As Cameron says to convert a function that takes say 2 parameters into a function that only takes 1. The result of calling this curried function with a parameter is the same as calling the original with the 2 parameters.
With Lambdas present in C# this has limited value since these can provide this effect anyway. Although it you are use C# 2 then the Curry function in your question has much greater value.
Staging computation
The other reason to curry is as I stated earlier. To allow complex/expensive operations to be staged and re-used several times when the final parameter(s) are supplied to the curried function.
This type of currying isn't truely possible in C#, it really takes a functional language that can natively curry any of its functions to acheive.
Conclusion
Parameter reduction via the Curry you mention is useful in C# 2 but is considerably de-valued in C# 3 due to Lambdas.
来自 codingforums 中的一个线程,
我特别喜欢对此进行解释的解释和长度此页面< /a>.
from a thread in codingforums
I particularly like the explanation and length at which this is explained on this page.
一个例子:您有一个函数compare(criteria1, criteria2, option1, option2, left, right)。 但是,当您想将函数
compare
提供给某些对列表进行排序的方法时,则compare()
必须只接受两个参数,compare(left, right)
。 使用 curry,您可以根据需要绑定条件参数来对该列表进行排序,最后这个高度可配置的函数将像任何其他普通compare(left,right)
一样呈现给排序算法。详细信息:.NET 委托采用隐式柯里化。 类的每个非静态成员函数都有一个隐式的
this
引用,但是,当您编写委托时,您不需要手动使用一些柯里化将this
绑定到功能。 相反,C# 关心语法糖,自动绑定 this,并返回一个只需要剩下参数的函数。在 C++ boost::bind 等中。 用于相同的用途。 与往常一样,在 C++ 中,一切都更加明确(例如,如果您想将实例成员函数作为回调传递,则需要显式绑定
this
)。One example: You have a function
compare(criteria1, criteria2, option1, option2, left, right)
. But when you want to supply the functioncompare
to some method with sorts a list, thencompare()
must only take two arguments,compare(left, right)
. With curry you then bind the criteria arguments as you need it for sorting this list, and then finally this highly configurable function presents to the sort algorithm as any other plaincompare(left,right)
.Detail: .NET delegates employ implicit currying. Each non-static member function of a class has an implicit
this
reference, still, when you write delegates, you do not need to manually use some currying to bindthis
to the function. Instead C# cares for the syntactic sugar, automatically binds this, and returns a function which only requires the arguments left.In C++ boost::bind et al. are used for the same. And as always, in C++ everything is a little bit more explicit (for instance, if you want to pass a instance-member function as a callback, you need to explicitly bind
this
).我有一个愚蠢的例子:
Uncurry 版本:
Curry 功能:
用法:
玩得开心!
I have this silly example:
Uncurry version:
Curry Function:
Usage:
Have fun!
这是如何使用 Curry 函数的另一个示例。 根据某些条件(例如一周中的某一天),您可以决定在更新文件之前应用什么存档策略。
here's another example of how you might use a Curry function. Depending on some condition (e.g. day of week) you could decide what archive policy to apply before updating a file.