我试图为我的客户提供多种访问排列的方法。我创建了以下代码,在每次访问时执行Action<T[]> output
:
public void Permute<T>(T[][] sets, Action<T[]> output)
{
Permute(sets, 0, new T[sets.Length], output);
}
private void Permute<T>(T[][] sets, int set, T[] permutation, Action<T[]> output)
{
for (int i = 0; i < sets[set].Length; ++i)
{
permutation[set] = sets[set][i];
if (set < (sets.Length - 1))
Permute(sets, set + 1, permutation, output);
else
output(permutation);
}
}
并且它有效,所以我转向访问排列的下一种方式,即使用IEnumerable<int[]>
和yield return
模式。这是我的实施:
public IEnumerable<int[]> Permute(int[][] sets)
{
return Permute(sets, 0, new int[sets.Length]); // <--skips this
}
private IEnumerable<int[]> Permute(int[][] sets, int set, int[] permutation)
{
for (int i = 0; i < sets[set].Length; ++i)
{
permutation[set] = sets[set][i];
if (set < (sets.Length - 1))
Permute(sets, set + 1, permutation);
else
yield return permutation;
}
}
但这不起作用。编译器跳过指定的代码行而不尝试执行它。
有人可以向我解释如何修改显示的代码,以便启用IEnumerable<int[]>
和yield return
模式吗?
以下是用以下方法测试它的客户端代码:(我使用的是nunit)
[Test]
public void PermuteThreeDifferentSetsUsingTheirIndexValues()
{
var stopwatch = new Stopwatch();
stopwatch.Start();
var indexSets = new[]
{
new[] {0, 1, 2},
new[] {0, 1, 2},
new[] {0, 1, 2},
};
var results = _generator.Permute(indexSets);
foreach (var result in results)
{
_permCounter++;
Console.Write(result[0]);
Console.Write(" ");
Console.Write(result[1]);
Console.Write(" ");
Console.Write(result[2]);
Console.WriteLine();
}
stopwatch.Stop();
Console.WriteLine("Permutation Visitor Elapsed Ticks: " + stopwatch.ElapsedTicks);
Assert.AreEqual(27, _permCounter);
}
我的预感是编译器不满意我不使用递归的返回值。然而,这只是一种预感。提前谢谢。
答案 0 :(得分:3)
这就是它的工作方式,以及大量代码使得自由使用:IEnumerable<T>
实现被允许是懒惰的。你的Permute
没有直接做任何工作:它返回一个对象,一旦你开始迭代它,开始做一些工作,并且只足以确定第一个结果。当你的循环然后请求下一个项目时,你的函数会继续,但只能直到它可以确定第二个结果。
这在enumerable.Where(some predicate).First()
等代码中非常有用,因为它可以避免在找到第一个结果后评估谓词。
在你最外面的函数中,你会迭代_generator.Permute(indexSets)
的结果,但在该函数中,你调用Permute(sets, set + 1, permutation)
并丢弃结果(如你在问题中所述)。由于您不使用该递归调用的结果,效果就好像递归调用永远不会发生。
一般来说,当你想在迭代器函数中递归调用自己时,你需要yield return
每个结果。一个愚蠢的例子:
IEnumerable<int> f(int n) {
if (n > 0)
foreach (var i in f(n - 1))
yield return i;
for (int i = 0; i != n; i++)
yield return i;
}