Howto: "letrec" в C # (вызов лямбда-выражения в его определении) - PullRequest
3 голосов
/ 06 апреля 2011

Рассмотрим функцию факториала, определенную в теле метода как лямбда-выражение и присвоенную переменной:

Func<int, int> factfail = n =>
{
    if (n == 0)
        return 1;
    else
        return n * factfail(n-1);
};

Это не удалось, поскольку factfail еще не ограничен локальной переменной.

Есть ли способ добавить некую точку фиксации - путем абстрагирования самой функции?!

Func<Func<int, int>, int, int> fact_ = (fact, n) => 
{  
    if (n == 0) 
        return 1;
    else 
        return n * fact(n-1); 
};

fact_(??);

длинная история: Мне нужно написать рекурсивную функцию, у которой есть побочный эффект изменения некоторого внешнего состояния. Поэтому я пытаюсь написать этот метод как лямбда-выражение, которое фиксирует это внешнее состояние.

Я все еще экспериментирую с разными стилями, как написать это и - помимо этого одного словаря, который должен быть одинаковым для всех рекурсивных вызовов - я хочу быть настолько функциональным и ленивым, насколько это возможно.

Так что я играл с LINQ, так как это помогает мне сократить взаимные данные. Это также помогает понять, какие части кода могут быть выражены в функциональном стиле.

Чтобы быть кратким в операторе LINQ, полезно иметь возможность определять некоторые вспомогательные функции перед ними, и я сделал это, связав лямбда-выражения с переменными.

И с помощью выражения lamda я также могу захватить свой словарь без необходимости явно передавать его ссылку на метод, что весьма неплохо.

не уверен, что я на правильном пути, хотя ...

1 Ответ

3 голосов
/ 06 апреля 2011

Вы можете найти больше информации о рекурсивных лямбда-выражениях в этом сообщении в блоге от Mads Torgersen. Он показывает, как определить обычный комбинатор с фиксированной точкой. В качестве примера он использует факториальную функцию, поэтому вы можете найти там точную выборку: -).

Однако на практике вы можете просто определить локальную переменную Func<..> и затем изменить ее. Если вы хотите дать имя делегату, то он работает просто отлично (немного грязно, но просто):

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

Это работает, потому что замыкание захватывает ссылку на переменную fact, поэтому, когда вы на самом деле вызываете ее (во время рекурсивного вызова), значение больше не null, а ссылка на делегат.

...