Может ли анонимный метод в C # вызывать сам себя? - PullRequest
52 голосов
/ 30 июля 2009

У меня есть следующий код:

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 # так, чтобы он мог вызывать сам себя?

Ответы [ 6 ]

90 голосов
/ 30 июля 2009

Вы можете разбить его на два оператора и использовать магию захваченных переменных для достижения эффекта рекурсии:

myDelegate build = null;
build = delegate(Object bj)
        {
           var letters= string.Empty;
           if (someCondition)
               return build(some_obj);                            
           else string.Empty;
        };
30 голосов
/ 30 июля 2009

Если вы создаете рекурсивную функцию, я бы рекомендовал избегать анонимных делегатов. Просто создайте метод и пусть он вызывает себя рекурсивно.

Анонимные методы должны быть анонимными - вы не должны вызывать их по имени (не анонимно).

24 голосов
/ 30 июля 2009

Анонимная рекурсия в C # имеет потрясающую дискуссию на эту тему.

Рекурсия прекрасна, и лямбды конечная абстракция. Но как можно они будут использоваться вместе? Лямбды анонимные функции и рекурсия требует имен ...

Так как это снова появилось, вот пример использования Y-комбинатора:

// 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-комбинатор и не настроите рекурсию только с делегатом, вы не получите правильную рекурсию. Например ...

// 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-комбинатор, он будет работать как положено ...

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 Link).

10 голосов
/ 30 июля 2009

Вы не можете вызвать build внутри build, так как тело анонимного метода является инициализацией самой переменной. Вы пытаетесь использовать переменную до ее определения.

Не то чтобы я рекомендовал это (поскольку было бы намного проще создать реальный метод здесь, который является рекурсивным), но если вам интересно, вы можете прочитать Анонимная рекурсия в C # :

Рекурсия прекрасна, и лямбды конечная абстракция. Но как можно они будут использоваться вместе? Лямбды анонимные функции и рекурсия требуются имена.

3 голосов
/ 05 октября 2010

Если вы используете 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);
  }
}
1 голос
/ 30 июля 2009

Если вы приближаетесь к рекурсивным анонимным методам, вы можете рекламировать его как обычный частный метод в вашем классе.

...