简体   繁体   English

递归IEnumerable不能按预期工作?

[英]Recursive IEnumerable doesn't work as expected?

I've written a recursive function which yields IEnumerable<int> 我编写了一个递归函数,它产生了IEnumerable<int>

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

So If I write 所以,如果我写

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

It should yield 它应该屈服

0
1
2
3

But it doesn't work as expected. 但它没有按预期工作。 ( it displays 0). (它显示0)。

In normal recursive function ( which return int - without Ienumerable) it works fine. 在正常的递归函数(返回int - 没有Ienumerable)中,它工作正常。

Question: 题:

How can I fix the code so it yields the expected value ? 如何修复代码以使其产生预期值?

nb. NB。 No there's no reason for using recursive Ienumerables. 不,没有理由使用递归的Ienumerables。 It's just came to my mind after playing with recursive yields. 在玩递归收益后,我才想到这一点。

Because you never yield b itself but only yield 0. 因为你永远不会屈服于b本身而只会屈服于0。

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

Note that if you want the resulting sequence to start from 0 and go upwards you have to yield return b after the foreach . 请注意,如果您希望结果序列从0开始向上,则必须在foreach 之后 yield return b Let's unroll the first call for Go(3) : 让我们展开Go(3)的第一个电话:

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

So 3 will be the last item in the sequence (because all the others are yieled before it). 所以3将是序列中的最后一项(因为所有其他项目之前都是yieled)。 Let's now unroll Go(2) : 我们现在展开Go(2)

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

Go(1) : 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;

As you can see, result are chained "backwards" with respect to the calls: 如您所见,结果相对于调用被“向后”链接:

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

I doubt that it would work anything different - because the only concrete yield I see is yield 0 我怀疑它会有什么不同 - 因为我看到的唯一具体yieldyield 0

I guess you want something like this: 我想你想要这样的东西:

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

but still this is highly inefficient and will blow the stack with bigger b s 但这仍然是非常低效的,并且将以更大的b s吹嘘堆栈


For your question: 对于你的问题:

Your code: 你的代码:

will do this: 会这样做:

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}

Yet another variant with condition b==0 条件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

}

or without yield break 或没有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
    }
}

Why your code doesn't work... Let's start from the end... You want [0, 1, 2, 3]. 为什么你的代码不起作用......让我们从最后开始......你想要[0,1,2,3]。 Clearly to obtain that sequence, there must be a 显然要获得该序列,必须有一个

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

But in your code you can: 但在您的代码中,您可以:

yield return 0

or 要么

yield return the Go function 

nowhere you have some code that can yield return the non-zero value! 无处你有一些代码可以返回非零值!

Note in fact that correct code has a 注意事实上正确的代码有一个

yield return b

where b is the value passed to the function Go(int b) , so that the function will first call itself recursively to return the valeus 0...b-1 and then yield the b value. 其中b是传递给函数Go(int b) ,因此函数将首先递归调用自身以返回valeus 0 ... b-1然后产生b值。

Let's assume you have an IEnumerable called enumerable. 假设您有一个名为enumerable的IEnumerable。 If you write 如果你写

foreach(var element in enumerable) yield return element;

is exactly the same as if you write 和你写的完全一样

return enumerable;

if you look at the result and the return type. 如果你看结果和返回类型。 If you try 如果你试试

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

It gives a compiler error: "Iterator cannot contain return statement", because if you write a function with a yield return statement in it, it won't compile to a function but an iterator, so it does not really "return". 它给出了编译器错误:“Iterator不能包含return语句”,因为如果你在其中编写一个带有yield return语句的函数,它将不会编译为函数而是迭代器,因此它不会真正“返回”。 Let's modify it to get the same behavior, but with a "real function" for clarity. 让我们修改它以获得相同的行为,但为了清晰起见,使用“实际功能”。 To make it compile, you could modify it to 要使其编译,您可以将其修改为

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

but it doesn't really make it more clear: What you did up there was almost like: 但它并没有真正说清楚:你在那里所做的几乎就像:

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

but the result is wrapped in an IEnumerable. 但结果包含在IEnumerable中。 I hope it's clear now why it returns only one 0. 我希望现在很清楚为什么它只返回一个0。

Adding visualization : ( just to show flow if yields) method name according to the b param 根据b参数添加可视化:(仅显示流量,如果产生)方法名称

took me a while to see what's going on here. 我花了一段时间看看这里发生了什么。

在此输入图像描述

Your code is flawed. 你的代码有缺陷。 :-) :-)

What it does is that the Go method only ever yields the value 0. If called with 3, then it gets down to the last recursive call whicht returns 0. The foreach iterates just that one 0 and yields it again. 它的作用是Go方法只产生值0.如果用3调用,那么它会下降到最后一个递归调用,它返回0. foreach只迭代那个0并再次产生它。 SO the zero is yielded on every level as the only element. 因此,在每个级别上都会产生零作为唯一元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM