9

LINQWhereではストリーミング演算子です。一方OrderByDescending、非ストリーミング演算子です。AFAIK、ストリーミングオペレーターは、必要な次のアイテムのみを収集します。非ストリーミングオペレータは、データストリーム全体を一度に評価します。

ストリーミング演算子を定義することの関連性がわかりません。私にとって、それは遅延実行と冗長です。カスタム拡張を記述し、where演算子とorderbyを使用してそれを使用した例を見てください。

public static class ExtensionStuff
{
    public static IEnumerable<int> Where(this IEnumerable<int> sequence, Func<int, bool> predicate)
    {
        foreach (int i in sequence)
        {
            if (predicate(i))
            {
                yield return i;
            }
        }
    }
}

    public static void Main()
    {
        TestLinq3();
    }

    private static void TestLinq3()
    {
        int[] items = { 1, 2, 3,4 };

        var selected = items.Where(i => i < 3)
                            .OrderByDescending(i => i);

        Write(selected);
    }



    private static void Write(IEnumerable<int> selected)
    {
        foreach(var i in selected)
            Console.WriteLine(i);
    }

いずれの場合も、Whereどの要素が条件を満たすかを判断するために、各要素を評価する必要があります。それが得られるという事実は、オペレーターが延期された実行を獲得するためにのみ関連するように思われます。

では、ストリーミング演算子の重要性は何ですか?

4

3 に答える 3

15

速度とメモリの2つの側面があります。

.Take()元の結果セットの一部のみを消費するような方法を使用すると、速度の側面がより明確になります。

// Consumes ten elements, yields 5 results.
Enumerable.Range(1, 1000000).Where(i => i % 2 == 0)
    .Take(5)
    .ToList();

// Consumes one million elements, yields 5 results.
Enumerable.Range(1, 1000000).Where(i => i % 2 == 0)
    .OrderByDescending(i => i)
    .Take(5)
    .ToList();

最初の例では、への呼び出しの前にストリーミング演算子のみを使用しているため、評価を停止するTake前に1〜10の値しか得られません。Takeさらに、一度に1つの値のみがメモリにロードされるため、メモリフットプリントは非常に小さくなります。

2番目の例では、がストリーミングされていないため、Takeが最初のアイテムをプルした瞬間に、フィルターをOrderByDescending通過した結果全体を並べ替えのためにメモリに配置する必要があります。Whereこれには長い時間がかかり、大きなメモリフットプリントが発生する可能性があります。

を使用していなくてもTake、メモリの問題が重要になる可能性があります。例えば:

// Puts half a million elements in memory, sorts, then outputs them.
var numbers = Enumerable.Range(1, 1000000).Where(i => i % 2 == 0)
    .OrderByDescending(i => i);
foreach(var number in numbers) Console.WriteLine(number);

// Puts one element in memory at a time.
var numbers = Enumerable.Range(1, 1000000).Where(i => i % 2 == 0);
foreach(var number in numbers) Console.WriteLine(number);
于 2012-04-05T21:11:45.903 に答える
2

それが得られるという事実は、オペレーターが延期された実行を獲得するためにのみ関連するように思われます。

では、ストリーミング演算子の重要性は何ですか?

つまり、バッファリング/非ストリーミング拡張メソッドで無限シーケンスを処理することはできませんでしたが、ストリーミング拡張メソッドのみを使用して、そのようなシーケンスを(中止するまで)問題なく「実行」できます。

たとえば、次の方法を考えてみましょう。

public IEnumerable<int> GetNumbers(int start)
{
    int num = start;

    while(true)
    {
        yield return num;
        num++;
    }
}

あなたはWhereうまく使うことができます:

foreach (var num in GetNumbers(0).Where(x => x % 2 == 0))
{
    Console.WriteLine(num);
}

OrderBy()この場合、単一の数値を出力する前に結果を徹底的に列挙する必要があるため、機能しません。

于 2012-04-05T21:12:25.170 に答える
2

明確にするために。あなたが言及した場合、とにかく orderby がすべてを吸い込むので、どこがストリームであるという事実には利点がありません。ただし、ストリーミングの利点が使用される場合があるため (他の回答/コメントに例が示されています)、すべての LINQ オペレーターが最大限の能力を発揮してストリーミングします。Orderby は可能な限りストリーミングしますが、たまたまそれほど多くはありません。ストリーミングが非常に効果的に行われる場所。

于 2012-04-05T21:26:28.630 に答える