我们在C#中需要定点组合器吗?

时间:2009-04-16 21:13:01

标签: c# functional-programming lambda

我正在使用C#中的递归lambdas,并且已经找到了两种在网络上执行此操作的方法。一种方法使用fixed point combinator而另一种方法不使用{{3}}。在下面的代码中,使用组合器构建f1,并直接定义f2。我的问题是,我们是否需要使用C#中的定点组合器,或者语言已经提供了我们所需要的所有内容,所以我们可以将它们单独留下来?

class Program
{
    static Func<T, T> F<T>(Func<Func<T,T>,Func<T,T>> f)
    {
        return x => f(F(f))(x);
    }

    static void Main(string[] args)
    {
        Func<Func<int,int>,Func<int,int>> f = fac => x => x == 0 ? 1 : x * fac(x - 1);
        var f1 = F(f);

        Console.WriteLine(f1(5));

        Func<int, int> f2 = null;
        f2 = x => x == 0 ? 1 : x * f2(x - 1);

        Console.WriteLine(f2(5));
    }
}

4 个答案:

答案 0 :(得分:4)

因为我们可以为方法命名,这意味着该语言已经为内置的递归提供了必要的支持。

请注意,问题中给出的第二种方法涉及在引入变量后更改变量的值,使其不是“纯粹的”函数式编程。 Y-combinator仅在您的函数微积分系统没有函数的内置概念时才需要,该函数可以在定义完全定义之前通过名称引用其自己的定义。 C#有两种直接方法:1。最初将函数变量定义为null,2。声明一个普通的命名方法(到目前为止是首选技术)。

答案 1 :(得分:3)

我认为使用名为Recursive的额外实用程序类可以相当优雅,如下所示:

public static class Recursive {
            public static Func<R> Func<R>(
                Func<Func<R>, Func<R>> f) { 
                return () => f(Func(f))(); }
            public static Func<T1, R> Func<T1, R>(
                Func<Func<T1, R>, Func<T1, R>> f) { 
                return x => f(Func(f))(x); }
            public static Func<T1, T2, R> Func<T1, T2, R>(
                Func<Func<T1, T2, R>, Func<T1, T2, R>> f) {
                return (a1, a2) => f(Func(f))(a1, a2);
            }
            //And so on...
        }

class Program {

    static void Main(string[] args) {

        Console.WriteLine(
            Recursive.Func<int, int>(factorial =>
                x => x == 0 ? 1 : factorial(x - 1) * x
            ) 
            (10)
        );

        Console.WriteLine(
            Recursive.Func<int,int,int>(gcd =>
                (x,y) => 
                    x == 0 ? y:
                    y == 0 ? x:
                    x > y  ? gcd(x % y, y):
                    gcd(y % x, x)
            )
            (35,21)
        );
    }
}

答案 2 :(得分:1)

另一种方法是将递归Func委托声明为静态成员:

static Func<int, int> Factorial = (n) => n <= 1 ? 1 : n*Factorial(n - 1);

答案 3 :(得分:-2)

“需要”是什么意思? C#不需要它们,因为你不应该在C#中尝试这种函数式编程。这只是痛苦的途径。

记住递归函数是一个你想要一个定点组合子的地方。将其与C#Haskell进行比较。

因此,在C#“需要”之前,需要做大量的工作才能使这种编程变得合理。