59

我有以下代码:

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# 中设置匿名方法以便它可以调用自己?

4

6 回答 6

97

您可以将其分解为两个语句,并使用捕获变量的魔力来实现递归效果:

myDelegate build = null;
build = delegate(Object bj)
        {
           var letters= string.Empty;
           if (someCondition)
               return build(some_obj);                            
           else string.Empty;
        };
于 2009-07-30T19:17:07.180 回答
31

如果您正在创建递归函数,我建议您避免使用匿名委托。只需创建一个方法并让它递归地调用自己。

匿名方法是匿名的——你不应该叫他们的名字(非匿名)。

于 2009-07-30T19:16:35.013 回答
25

C# 中的匿名递归对此主题进行了精彩的讨论。

递归很漂亮,而 lambdas 是终极抽象。但是它们怎么能一起使用呢?Lambda 是匿名函数,递归需要名称...

由于这再次弹出,这里是使用 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 链接)。

于 2009-07-30T19:18:03.027 回答
10

您不能在其build内部调用,build因为匿名方法的主体是变量本身的初始化。您试图在定义变量之前使用它。

并不是我推荐这个(因为在这里创建一个真正的递归方法会更简单),但如果你有兴趣,你可以阅读C# 中的匿名递归

递归很漂亮,而 lambdas 是终极抽象。但是它们怎么能一起使用呢?Lambda 是匿名函数,递归需要名称。

于 2009-07-30T19:16:56.120 回答
3

如果您使用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);
  }
}
于 2010-10-05T16:29:54.447 回答
1

如果您正在使用递归匿名方法,您可能希望将其提升为您班级中的普通私有方法。

于 2009-07-30T19:17:22.110 回答