递归 IEnumerable 没有按预期工作?
Recursive IEnumerable doesn't work as expected?
我写了一个递归函数,它产生 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)。
在正常的递归函数中(return int - 没有 Ienumerable)它工作正常。
问题:
如何修改代码以使其产生预期值?
注意。不,没有理由使用递归 Ienumerables。在玩了递归收益率之后,我才想到它。
我怀疑它是否会有所不同 - 因为我看到的唯一具体 yield
是 yield 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}
因为你永远不会产生 b 本身,只会产生 0。
IEnumerable<int> Go(int b)
{
if(b > 0) {
foreach (var element in Go(b-1))
yield return element;
}
yield return b;
}
请注意,如果您希望结果序列从 0 开始并向上移动,您必须 yield return b
在 之后 foreach
。让我们展开对 Go(3)
:
的第一个调用
Go(3):
foreach(var element in Go(2)):
yield return element;
yield return 3;
因此 3 将是序列中的最后一项(因为所有其他项都在它之前产生)。现在展开 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;
如您所见,结果与调用相关联 "backwards":
Go(3) --> Go(2) --> Go(1) --> Go(0) --> 0 --> 1 --> 2 --> 3
您的代码有缺陷。 :-)
它所做的是 Go 方法只产生值 0。如果用 3 调用,那么它会下降到最后一个递归调用 returns 0。foreach 只迭代那个 0 和再次产生它。因此,零作为唯一元素在每个级别上产生。
假设您有一个名为 enumerable 的 IEnumerable。如果你写
foreach(var element in enumerable) yield return element;
和你写的完全一样
return enumerable;
如果您查看结果和 return 类型。如果你尝试
if(b == 0) yield return 0;
else return Go(b - 1);
它给出了一个编译器错误:"Iterator cannot contain return statement",因为如果你写一个带有 yield return 语句的函数,它不会编译成一个函数而是一个迭代器,所以它会不是真的 "return"。让我们修改它以获得相同的行为,但为了清楚起见使用 "real function"。要使其编译,您可以将其修改为
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 中。我希望现在清楚为什么它 return 只有一个 0.
为什么您的代码不起作用...让我们从头开始...您想要 [0, 1, 2, 3]。显然要得到那个序列,必须有一个
yield return 0
yield return 1
yield return 2
yield return 3
但是在您的代码中您可以:
yield return 0
或
yield return the Go function
没有任何代码可以产生 return 非零值!
请注意,事实上正确的代码有一个
yield return b
其中 b 是传递给函数 Go(int b)
的值,因此该函数将首先递归调用自身以 return 值 0...b-1 然后产生 b 值.
条件为 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
}
}
根据 b
参数
添加可视化:(只是为了显示流程,如果产量)
我花了一些时间才看到这里发生了什么。
我写了一个递归函数,它产生 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)。
在正常的递归函数中(return int - 没有 Ienumerable)它工作正常。
问题:
如何修改代码以使其产生预期值?
注意。不,没有理由使用递归 Ienumerables。在玩了递归收益率之后,我才想到它。
我怀疑它是否会有所不同 - 因为我看到的唯一具体 yield
是 yield 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}
因为你永远不会产生 b 本身,只会产生 0。
IEnumerable<int> Go(int b)
{
if(b > 0) {
foreach (var element in Go(b-1))
yield return element;
}
yield return b;
}
请注意,如果您希望结果序列从 0 开始并向上移动,您必须 yield return b
在 之后 foreach
。让我们展开对 Go(3)
:
Go(3):
foreach(var element in Go(2)):
yield return element;
yield return 3;
因此 3 将是序列中的最后一项(因为所有其他项都在它之前产生)。现在展开 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;
如您所见,结果与调用相关联 "backwards":
Go(3) --> Go(2) --> Go(1) --> Go(0) --> 0 --> 1 --> 2 --> 3
您的代码有缺陷。 :-)
它所做的是 Go 方法只产生值 0。如果用 3 调用,那么它会下降到最后一个递归调用 returns 0。foreach 只迭代那个 0 和再次产生它。因此,零作为唯一元素在每个级别上产生。
假设您有一个名为 enumerable 的 IEnumerable。如果你写
foreach(var element in enumerable) yield return element;
和你写的完全一样
return enumerable;
如果您查看结果和 return 类型。如果你尝试
if(b == 0) yield return 0;
else return Go(b - 1);
它给出了一个编译器错误:"Iterator cannot contain return statement",因为如果你写一个带有 yield return 语句的函数,它不会编译成一个函数而是一个迭代器,所以它会不是真的 "return"。让我们修改它以获得相同的行为,但为了清楚起见使用 "real function"。要使其编译,您可以将其修改为
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 中。我希望现在清楚为什么它 return 只有一个 0.
为什么您的代码不起作用...让我们从头开始...您想要 [0, 1, 2, 3]。显然要得到那个序列,必须有一个
yield return 0
yield return 1
yield return 2
yield return 3
但是在您的代码中您可以:
yield return 0
或
yield return the Go function
没有任何代码可以产生 return 非零值!
请注意,事实上正确的代码有一个
yield return b
其中 b 是传递给函数 Go(int b)
的值,因此该函数将首先递归调用自身以 return 值 0...b-1 然后产生 b 值.
条件为 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
}
}
根据 b
参数
我花了一些时间才看到这里发生了什么。