递归IEnumerable没有按预期工作?

时间:2015-03-18 12:56:18

标签: c# recursion ienumerable

我编写了一个递归函数,产生IEnumerable<int>

IEnumerable<int>   Go(int b  )
{
 if (b==0) yield  return 0;
   else
 foreach (var element in Go(b-1))
  {
    yield return element;
  }
}

所以,如果我写

foreach (var element in Go(3))
{
    Console.WriteLine (element);
}

它应该产生

0
1
2
3

但它并没有像预期的那样发挥作用。 (它显示0)。

在正常的递归函数中(返回int - 没有Ienumerable),它可以正常工作。

问题:

如何修复代码以使其产生预期值?

NB。没有理由使用递归的Ienumerables。在使用递归收益后,我才想起它。

7 个答案:

答案 0 :(得分:11)

因为你自己从不屈服于b而只屈服于0。

IEnumerable<int> Go(int b)
{
    if(b > 0) {
         foreach (var element in Go(b-1))
            yield return element;
    }
    yield return b;
}

请注意,如果您希望结果序列从0开始向上,则必须在<{em> yield return b之后foreach 。让我们展开Go(3)的第一个电话:

Go(3):
foreach(var element in Go(2)):
    yield return element;
yield return 3;

因此3将是序列中的最后一项(因为所有其他项目之前都是yieled)。现在让我们展开Go(2)

Go(3):
    Go(2):
    foreach(var element in Go(1)):
        yield return element;
    yield return 2;
yield return 3;

Go(1)

Go(3):
    Go(2):
        Go(1):
            foreach(var element in Go(0))
                yield return element;
        yield return 1;
    yield return 2;
yield return 3;

正如您所看到的,结果是链接的&#34;向后&#34;关于电话:

Go(3) --> Go(2) --> Go(1) --> Go(0) --> 0 --> 1 --> 2 --> 3

答案 1 :(得分:6)

我怀疑它会有什么不同 - 因为我看到的唯一具体yieldyield 0

我猜你想要这样的东西:

IEnumerable<int> Go(int b)
{
   if (b > 0)
   {
      foreach (var element in Go(b-1))
      {
        yield return element;
      }
   }
   yield return b;
}

但这仍然非常低效,并且会在更大的b s

的情况下炸毁堆栈

对于你的问题:

您的代码:

会这样做:

b=3:
  is b == 0? no ok, then enumerate and return everything from b=2...
     b=2:
       is b == 0? no ok, then enumerate and return everything from b=1...
         b=1:
           is b == 0? no ok, then enumerate everything from b=0...
             b=0:
               is b == 0? **YES** so yield a single **0**
           everything was {0}
       everything was {0}
  everything was {0}
return is {0}

答案 2 :(得分:4)

条件为b==0

的另一种变体
static IEnumerable<int> Go(int b)
{
    if (b == 0)
    {
        yield return 0; //return 0 if b==0;
        yield break; // say that iteration end;
    }

    foreach (var el in Go(b - 1)) 
    {
        yield return el;
    }

    yield return b; //return current b as element of result collection

}

或没有yield break

static IEnumerable<int> Go(int b)
{
    if (b == 0)
    {
        yield return 0;
    }
    else
    {

        foreach (var el in Go(b - 1)) 
        {
            yield return el;
        }

        yield return b; //return current b as element of result collection
    }
}

答案 3 :(得分:2)

为什么你的代码不起作用......让我们从最后开始...你想要[0,1,2,3]。显然要获得该序列,必须有一个

yield return 0
yield return 1
yield return 2
yield return 3

但在您的代码中,您可以:

yield return 0

yield return the Go function 

无处有一些代码可以返回非零值!

请注意,正确的代码有

yield return b

其中b是传递给函数Go(int b)的值,因此函数将首先递归调用自身以返回valeus 0 ... b-1然后产生b值。

答案 4 :(得分:1)

假设您有一个名为enumerable的IEnumerable。如果你写

foreach(var element in enumerable) yield return element;

与编写

完全相同
return enumerable;

如果查看结果和返回类型。如果你试试

if(b == 0) yield return 0;
else return Go(b - 1);

它给出了编译器错误:“Iterator不能包含return语句”,因为如果你在其中编写一个带有yield return语句的函数,它将不会编译为函数而是迭代器,所以它不会真正“返回” ”。让我们修改它以获得相同的行为,但为了清晰起见,使用“实际功能”。要使其编译,您可以将其修改为

if (b == 0) return Enumerable.Repeat(0, 1); // or return Enumerable.Range(0, 1);
else return Go(b - 1);

但它并没有真正说清楚:你在那里所做的几乎就像:

return b == 0 ? 0 : Go(b-1);

但结果包含在IEnumerable中。我希望现在很清楚为什么它只返回一个0。

答案 5 :(得分:1)

根据b param添加可视化:(仅显示流量,如果产量)方法名称

花了一些时间看看这里发生了什么。

enter image description here

答案 6 :(得分:0)

您的代码存在缺陷。 : - )

它的作用是Go方法只产生值0.如果用3调用,那么它将返回到最后一个递归调用,它返回0. foreach只迭代一个0并再次产生它。因此,在每个级别上都会产生零作为唯一元素。