C#Linq vs.Currying
我正在玩一点函数式编程和它的各种概念。所有这些东西都很有趣。我已经读过好几次关于咖喱的书,以及它有多大的优势 但我不明白这一点。下面的源代码演示了curry概念的使用以及linq的解决方案。事实上,我看不到任何使用咖喱概念的建议 那么,使用咖喱有什么好处呢C#Linq vs.Currying,c#,linq,currying,C#,Linq,Currying,我正在玩一点函数式编程和它的各种概念。所有这些东西都很有趣。我已经读过好几次关于咖喱的书,以及它有多大的优势 但我不明白这一点。下面的源代码演示了curry概念的使用以及linq的解决方案。事实上,我看不到任何使用咖喱概念的建议 那么,使用咖喱有什么好处呢 static bool IsPrime(int value) { int max = (value / 2) + 1; for (int i = 2; i < max; i++) { if ((v
static bool IsPrime(int value)
{
int max = (value / 2) + 1;
for (int i = 2; i < max; i++)
{
if ((value % i) == 0)
{
return false;
}
}
return true;
}
static readonly Func<IEnumerable<int>, IEnumerable<int>> GetPrimes =
HigherOrder.GetFilter<int>().Curry()(IsPrime);
static void Main(string[] args)
{
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
Console.Write("Primes:");
//Curry
foreach (int n in GetPrimes(numbers))
{
Console.Write(" {0}", n);
}
Console.WriteLine();
//Linq
foreach (int n in numbers.Where(p => IsPrime(p)))
{
Console.Write(" {0}", n);
}
Console.ReadLine();
}
static bool IsPrime(int值)
{
int max=(值/2)+1;
对于(int i=2;iIsPrime(p)))
{
Console.Write(“{0}”,n);
}
Console.ReadLine();
}
以下是高阶过滤方法:
public static Func<Func<TSource, bool>, IEnumerable<TSource>, IEnumerable<TSource>> GetFilter<TSource>()
{
return Filter<TSource>;
}
public static Func GetFilter()
{
回流过滤器;
}
@Eric Lippert对本博客帖子的评论:
我发现这是最适合我的解释:
从理论上讲,这很有趣,因为它(咖喱)简化了
lambda演算只包含那些最多
一个论点。从实用的角度来看,它允许程序员
通过固定
首先是k参数。这就像把什么东西钉在墙上
这需要两个引脚。在固定对象之前,对象可以自由移动
在地面上的任何地方移动;但是,当插入第一个销时
然后,移动受到约束。最后,当第二个引脚被放置时
那时,就不再有任何行动自由。同样,当
程序员将两个参数的函数转换为curry函数,并将其应用于
首先是参数,然后功能受限于一个维度。
最后,当他将新函数应用于第二个参数时
计算特定值
进一步讲,我看到函数式编程本质上引入了“数据流编程而不是控制流”,这类似于使用say SQL而不是C#。有了这个定义,我明白了为什么LINQ是,为什么它有许多纯LINQ2对象之外的应用程序,比如Rx中的事件。使用curry的优势主要体现在函数式语言中,函数式语言是为了从curry中获益而构建的,并且具有方便的语法。C#不是这样一种语言,用C#实现咖喱通常很难理解,就像表达式
HigherOrder.GetFilter().Curry()(IsPrime)
一样
使用咖喱有什么好处
static bool IsPrime(int value)
{
int max = (value / 2) + 1;
for (int i = 2; i < max; i++)
{
if ((value % i) == 0)
{
return false;
}
}
return true;
}
static readonly Func<IEnumerable<int>, IEnumerable<int>> GetPrimes =
HigherOrder.GetFilter<int>().Curry()(IsPrime);
static void Main(string[] args)
{
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
Console.Write("Primes:");
//Curry
foreach (int n in GetPrimes(numbers))
{
Console.Write(" {0}", n);
}
Console.WriteLine();
//Linq
foreach (int n in numbers.Where(p => IsPrime(p)))
{
Console.Write(" {0}", n);
}
Console.ReadLine();
}
首先,让我们澄清一些术语。人们用“咖喱”来表示以下两种意思:
static int Add(int x, int y) { return x + y; }
你可以这样称呼它:
int result = Add(2, 3); // 5
您可以使用Add
方法:
static Func<int, int> MakeAdder(int x) { return y => Add(x, y); }
但是,当您实际构建算法时,您通常想要知道的是当前位置与固定端点之间的距离。算法需要的是Func
——从位置到固定端点的距离是多少?您拥有的是Func
。你打算如何把你所拥有的变成你所需要的?局部应用;部分地将固定端点作为第一个参数应用于近似距离方法,并得到一个与路径查找算法需要使用的函数相匹配的函数:
Func<Point, double> distanceFinder = PartiallyApply<Point, Point, double>(ApproximateDistance, givenEndPoint);
除了混淆代码之外,我看不到任何明显的好处……使用Linq,您甚至可以编写
数字。Where(IsPrime)
我在您的示例代码中没有看到任何咖喱。还有,什么是HigherOrder
?@leppie:HigherOrder是我自己的一个类,有几个helper方法。你可以看到从GetFilter()到GetPrimes()的转换。@phoog:这听起来像是一种完全不做任何事情的糟糕方式!我认为,在C#中,人们最终仍然偶尔需要使用curry函数,但他们通常通过手动使用特定于curry的函数(即编写返回委托的函数)而不是使用一般的curry函数来解决这一需要。咖喱仍然在使用,只是不是以自动的方式。@Brian这是一个很好的观点,尤其是随着lambda表达式的出现。Eric,你还知道这个概念的名称吗,其中一个函数返回a
取B
,另一个函数返回B
并取C
,因此基于此,可能还有另一个函数接受C
并返回A
。“我不确定我是否正确地解释了它,但它是一个功能性概念。”JoanVenge称之为。
static Func<B, R> PartiallyApply<A, B, R>(Func<A, B, R> f, A a)
{
return (B b)=>f(a, b);
}
...
Func<int, int> addTwo = PartiallyApply<int, int, int>(Add, 2);
int result = addTwo(3); // 5
static double ApproximateDistance(Point p1, Point p2) { ... }
Func<Point, double> distanceFinder = PartiallyApply<Point, Point, double>(ApproximateDistance, givenEndPoint);
static Func<Point, double> MakeApproximateDistanceFinder(Point p1) { ... }
Func<Point, double> distanceFinder = MakeApproximateDistanceFinder(givenEndPoint);