Может ли анонимный метод в C # вызывать сам себя?
-
05-07-2019 - |
Вопрос
У меня есть следующий код:
class myClass
{
private delegate string myDelegate(Object bj);
protected void method()
{
myDelegate build = delegate(Object bj)
{
var letters= string.Empty;
if (someCondition)
return build(some_obj); //This line seems to choke the compiler
else string.Empty;
};
......
}
}
Есть ли другой способ настроить анонимный метод в C # таким образом, чтобы он мог вызывать сам себя?
Решение
Вы можете разбить его на два оператора и использовать магию захваченных переменных для достижения эффекта рекурсии:
myDelegate build = null;
build = delegate(Object bj)
{
var letters= string.Empty;
if (someCondition)
return build(some_obj);
else string.Empty;
};
Другие советы
Если вы создаете рекурсивную функцию, я бы рекомендовал избегать анонимных делегатов. Просто создайте метод и пусть он вызывает себя рекурсивно.
Анонимные методы должны быть анонимными - вы не должны вызывать их по имени (не анонимно).
Анонимная рекурсия в C# ведет потрясающую дискуссию на эту тему.
Рекурсия прекрасна, а лямбды - это высшая абстракция.Но как их можно использовать вместе?Лямбды - это анонимные функции, а рекурсия требует имен...
Поскольку это снова появилось, вот пример использования Y-combinator:
// This is the combinator
public static Func<A,R> Y<A,R>( Func<Func<A,R>, Func<A,R>> f )
{
Func<A,R> g = null;
g = f( a => g(a) );
return g;
}
Вот как это используется для вызова анонимной рекурсивной функции...
Func<int,int> exp = Y<int,int>( e => x => ( x <=1 ) ? 1 : x * e( x - 1 ) );
Console.WriteLine( exp(5) );
Вы заметите, что если вы не используете Y-combinator и настраиваете рекурсию только с помощью делегата, вы не получите правильную рекурсию.Например ...
// This is BAD. Do not do this!
Func<int,int> badRec = null;
badRec = x => ( x <= 1 ) ? 1 : x * badRec( x - 1 );
Но все работает нормально ...
Console.WriteLine( badRec(5) );
// Output
// 120
Но попробуй вот что ...
Func<int,int> badRec = null;
badRec = x => ( x <= 1 ) ? 1 : x * badRec( x - 1 );
Func<int,int> badRecCopy = badRec;
badRec = x => x + 1;
Console.WriteLine( badRec(4) );
Console.WriteLine( badRecCopy(5) );
// Output
// 5
// 25
Что?!?
Видите ли, после строчки badRec = x => x + 1;
, делегат , который у вас на самом деле есть , это ...
badRecCopy = x => ( x <= 1 ) ? 1 : x * ( (x+1)-1 );
Итак, badRec увеличивает значение на 1, которое мы ожидаем (4+1=5)
, но badRecCopy теперь фактически возвращает квадрат значения (5*( (5+1)-1 )
чего мы почти наверняка не ожидали.
Если вы используете Y-combinator, он будет работать так, как ожидалось...
Func<int,int> goodRec = Y<int,int>( exp => x => ( x <=1 ) ? 1 : x * exp( x - 1 ) );
Func<int,int> goodRecCopy = goodRec;
И вы получаете то, чего ожидаете.
goodRec = x => x + 1;
Console.WriteLine( goodRec(4) );
Console.WriteLine( goodRecCopy(5) );
// Output
// 5
// 120
Вы можете прочитать больше о Y-комбинатор (Ссылка в формате PDF).
Вы не можете вызывать build
внутри самого build
, поскольку тело анонимного метода является инициализацией самой переменной. Вы пытаетесь использовать переменную до ее определения.
Я не рекомендую это (поскольку было бы гораздо проще создать реальный метод здесь, который является рекурсивным), но если вам интересно, вы можете прочитать Анонимная рекурсия в C # :
Рекурсия прекрасна, и лямбды конечная абстракция. Но как можно они будут использоваться вместе? Лямбды анонимные функции и рекурсия требует имен.
Если вы используете Y , ваша функция становится параметром самой функции, поэтому вы можете вызывать ее рекурсивно:
class myClass {
private delegate string myDelegate(Object bj);
protected void method() {
myDelegate build = delegate(Object obj) {
// f is the function itself, which is passed into the function
return Functional.Y<Object, string>(f => bj => {
var letters = string.Empty;
if (someCondition)
return f(some_obj); // use f
else return string.Empty;
})(obj);
};
}
}
public static class Functional {
public delegate Func<A, R> Recursive<A, R>(Recursive<A, R> r);
public static Func<A, R> Y<A, R>(Func<Func<A, R>, Func<A, R>> f) {
Recursive<A, R> rec = r => a => f(r(r))(a);
return rec(rec);
}
}
Если вы приближаетесь к рекурсивным анонимным методам, вы можете рекламировать его как обычный частный метод в своем классе.