Howto: "letrec" в C# (вызов лямбда-выражения в его определении)
Рассмотрим функцию факториала, определенную в теле метода как лямбда-выражение и присвоенную переменной:
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 ответ
Вы можете найти больше информации о рекурсивных лямбда-выражениях в этом сообщении в блоге Mads Torgersen. Он показывает, как определить обычный комбинатор с фиксированной точкой. Он использует факториальную функцию в качестве примера, поэтому вы можете найти там точный пример:-).
Однако на практике вы можете просто определить местный Func<..>
переменная, а затем изменить его. Если вы хотите дать имя делегату, то он работает просто отлично (он немного грязный, но простой):
Func<int, int> fact = null;
fact = (n) => (n == 0) ? 1 : n * fact(n-1);
Это работает, потому что замыкание захватывает ссылку на fact
переменная, поэтому, когда вы на самом деле вызываете ее (во время рекурсивного вызова), значение не null
больше, но ссылается на делегата.