[英]Why does the compiler skip this method?
我正在嘗試為客戶提供多種訪問排列的方式。 我創建了以下代碼,每次執行時都執行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);
}
我的直覺是,編譯器對我不使用遞歸的返回值不滿意。 但是,這只是預感。 先感謝您。
那就是它的設計工作方式,以及大量的代碼可以自由使用: 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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.