web-dev-qa-db-ja.com

C#の匿名メソッドはそれ自体を呼び出すことができますか?

私は次のコードを持っています:

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#で匿名メソッドを設定する別の方法はありますか?

53
Matt

これを2つのステートメントに分割し、キャプチャされた変数の魔法を使用して、再帰効果を実現できます。

myDelegate build = null;
build = delegate(Object bj)
        {
           var letters= string.Empty;
           if (someCondition)
               return build(some_obj);                            
           else string.Empty;
        };
92
Mehrdad Afshari

再帰関数を作成する場合は、匿名のデリゲートを避けることをお勧めします。メソッドを作成し、それ自体を再帰的に呼び出すだけです。

匿名メソッドは匿名であることが意図されています-名前で(非匿名で)呼び出すべきではありません。

30
Reed Copsey

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は実際には値の2乗を返しています(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-combinator (PDFリンク)の詳細を読むことができます。

24
JP Alioto

匿名メソッドの本体は変数自体の初期化であるため、build自体の内部でbuildを呼び出すことはできません。定義される前に変数を使用しようとしています。

これをお勧めするわけではありませんが(はるか再帰的である実際のメソッドをここで作成する方が簡単です)、興味がある場合は C#での匿名再帰 :を読むことができます。

再帰は美しく、ラムダは究極の抽象化です。しかし、どうすればそれらを一緒に使用できますか?ラムダは無名関数であり、再帰には名前が必要です。

10
Andrew Hare

[〜#〜] 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);
  }
}
3
Jordão

再帰的な匿名メソッドのポイントに到達している場合は、クラス内の通常のプライベートメソッドに昇格させることをお勧めします。

1
Will Eddins