我是 C# 的新手,遇到了一個有趣的 Linq 行為,如下所示。
FlipNumber具有預期的行為,按索引選擇Select((_, i) ...)顯示了翻轉數字字串n.Length - 1次數的正確行為,而FlipNumber1僅在按專案選擇時評估一次并Last()獲得最后一次迭代結果。
誰能幫忙解釋一下?
使用以下代碼進行簡單測驗(忘記效率plz)
using System;
using System.Linq;
public class Test
{
public static string FlipNumber(string n)
{
Console.WriteLine($"FlipNumber");
return Enumerable.Range(0, n.Length - 1).Select((_, i) => {
n = new string(n.Take(i).Concat(n.Skip(i).Reverse()).ToArray());
Console.WriteLine($"{i} : {n}");
return n;
}).Last();
}
public static string FlipNumber1(string n)
{
Console.WriteLine($"FlipNumber1");
return Enumerable.Range(0, n.Length - 1).Select(i => {
n = new string(n.Take(i).Concat(n.Skip(i).Reverse()).ToArray());
Console.WriteLine($"{i} : {n}");
return n;
}).Last();
}
// suggested by @DiplomacyNotWar
// add `ToList()` before `Last()` makes the result same with FlipNumber
public static string FlipNumber11(string n)
{
Console.WriteLine($"FlipNumber11");
return Enumerable.Range(0, n.Length - 1).Select(i => {
n = new string(n.Take(i).Concat(n.Skip(i).Reverse()).ToArray());
Console.WriteLine($"{i} : {n}");
return n;
}).ToList().Last();
}
public static string FlipNumber2(string n)
{
Console.WriteLine($"FlipNumber2");
return n.Select(i => {
n = new string(n.Take(i).Concat(n.Skip(i).Reverse()).ToArray());
Console.WriteLine($"{i} : {n}");
return n;
}).Last();
}
public static void Main(string[] args)
{
FlipNumber("123456789");
FlipNumber1("123456789");
FlipNumber11("123456789");
FlipNumber2("123456789");
}
}
/*
Results:
FlipNumber
0 : 987654321
1 : 912345678
2 : 918765432
3 : 918234567
4 : 918276543
5 : 918273456
6 : 918273654
7 : 918273645
FlipNumber1
7 : 123456798
FlipNumber11
0 : 987654321
1 : 912345678
2 : 918765432
3 : 918234567
4 : 918276543
5 : 918273456
6 : 918273654
7 : 918273645
FlipNumber2
1 : 123456789
2 : 123456789
3 : 123456789
4 : 123456789
5 : 123456789
6 : 123456789
7 : 123456789
8 : 123456789
9 : 123456789
*/
uj5u.com熱心網友回復:
這歸結為 LINQ 中的一些優化。當我們檢查源代碼時,Last我們看到它是這樣的:
public static TSource Last<TSource>(this IEnumerable<TSource> source)
{
bool found;
TSource result = source.TryGetLast(out found);
if (!found)
{
ThrowHelper.ThrowNoElementsException();
}
return result;
}
我們遵循它TryGetLast并看到它是這樣的:
private static TSource TryGetLast<TSource>(this IEnumerable<TSource> source, out bool found)
{
if (source == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
IPartition<TSource> partition = source as IPartition<TSource>;
if (partition != null)
{
return partition.TryGetLast(out found);
}
IList<TSource> list = source as IList<TSource>;
if (list != null)
{
int count = list.Count;
if (count > 0)
{
found = true;
return list[count - 1];
}
}
else
{
using IEnumerator<TSource> enumerator = source.GetEnumerator();
if (enumerator.MoveNext())
{
TSource current;
do
{
current = enumerator.Current;
}
while (enumerator.MoveNext());
found = true;
return current;
}
}
found = false;
return default(TSource);
}
最后是IPartition<TSource>:
internal interface IPartition<TElement> : IIListProvider<TElement>, IEnumerable<TElement>, IEnumerable
{
IPartition<TElement> Skip(int count);
IPartition<TElement> Take(int count);
TElement TryGetElementAt(int index, out bool found);
TElement TryGetFirst(out bool found);
TElement TryGetLast(out bool found);
}
引擎蓋下回傳 a ,它實作了Select(i =>它能夠跳轉到最后一個元素。SelectIPartitionIteratorIPartition<TElement>
當您這樣做時Select((_, i) =>,它不會創建一個IPartition<TSource>,因此它被迫進行完整的迭代。
在這個問題中的整個使用n =只是一個furphy,應該避免。它把一個很好的問題變成了一個隱藏真實情況的問題。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/510709.html
標籤:C#林克
