C#Linq vs.Currying

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

我正在玩一点函数式编程和它的各种概念。所有这些东西都很有趣。我已经读过好几次关于咖喱的书,以及它有多大的优势

但我不明白这一点。下面的源代码演示了curry概念的使用以及linq的解决方案。事实上,我看不到任何使用咖喱概念的建议

那么,使用咖喱有什么好处呢

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();
}
首先,让我们澄清一些术语。人们用“咖喱”来表示以下两种意思:

  • 将两个参数的方法重新格式化为一个参数的方法,该方法返回一个参数的方法,然后
  • 部分应用两个参数的方法生成一个参数的方法
  • 显然,这两项任务是密切相关的,因此造成了混乱。正式发言时,应该限制“currying”指的是第一个定义,但非正式发言时,这两种用法都很常见

    因此,如果您有一个方法:

    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);