Нужны ли нам комбинаторы с фиксированной точкой в ​​C #? - PullRequest
3 голосов
/ 17 апреля 2009

Я играл с рекурсивными лямбдами в C # и нашел два подхода для этого в Интернете. Один подход использует комбинатор с фиксированной точкой , а другой - нет. В приведенном ниже коде 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 ]

4 голосов
/ 17 апреля 2009

Поскольку мы можем дать имя методу, это означает, что в язык уже встроена необходимая поддержка рекурсии.

Обратите внимание, что второй метод, приведенный в вашем вопросе, включает изменение значения переменной после ее введения, что делает ее не "чистым" функциональным программированием. Y-комбинатор необходим только в том случае, если ваша система функционального исчисления не имеет встроенного понятия функции, которая может ссылаться на свое собственное определение по имени до того, как определение будет полностью определено. В C # есть два способа сделать это напрямую: 1. изначально определить переменную функции как null и 2. объявить обычный именованный метод (безусловно, предпочтительный метод).

3 голосов
/ 19 декабря 2009

Я думаю, что это может быть довольно элегантно с дополнительным классом утилит под названием 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)
        );
    }
}
1 голос
/ 17 апреля 2009

Другой альтернативой является объявление ваших рекурсивных делегатов Func в качестве статических членов:

static Func<int, int> Factorial = (n) => n <= 1 ? 1 : n*Factorial(n - 1);
0 голосов
/ 17 апреля 2009

Что значит «нужно»? C # не нуждается в них, так как вы не должны пытаться такого рода функциональное программирование в C #. Это просто путь к боли.

Запоминание рекурсивной функции - это то место, где вам нужен комбинатор с фиксированной точкой. Сравните это в C # с Haskell .

Итак, прежде чем C # это "понадобится", ему нужно проделать большую работу, чтобы сделать этот вид программирования достаточно практичным.

...