2015-05-21 3 views
0

Верно ли, что случайные данные внутри ленивого оператора могут оцениваться по-разному во время выполнения? С помощью следующего кода я вижу «wow» многократно напечатанную на консоли. Однако, если я заставляю результат запроса (т. Е. Вызывать ToList() по номерам xs и ys), все выглядит нормально.Случайные числа в запросах linq

public static void Main(string[] args) 
    { 
     var generator = new Random(); 
     var xs = from x in Enumerable.Range(0, 20000) 
       select generator.Next(); 

     var ys = from y in Enumerable.Range(0, 5000) 
       select generator.Next(); 

     foreach (var x in xs) 
     { 
      var q1 = from y in ys where y > x select y; 
      var q2 = from y in ys where y > x select y; 

      if (!q1.SequenceEqual(q2)) 
       Console.WriteLine("wow!"); 
     } 

     Console.WriteLine("done"); 
     Console.ReadLine(); 

    } 

Я подозреваю, что это связано с тем, что запросы linq являются «ленивыми». Это точно?

+0

«отложенное выполнение» запросов LINQ является одной из наиболее распространенных непонятых функций. Запрос всегда выполняется, когда переменная запроса повторяется, а не когда создается переменная запроса. –

+0

Я действительно хочу найти ответ, который «исправляет» это, используя «новый случайный» для каждой итерации 'ys', но я не могу понять, как это сделать, не делая этого очевидным :( – Rawling

+0

@ Rawling 'from x in Enumerable.Range (0, 20000) let rnd = new Random() select rnd.Next(); ' – xanatos

ответ

2

Верно ли, что случайные данные внутри ленивого оператора могут оцениваться по-разному во время выполнения?

Что верно, то, что, как вы написали:

Я подозреваю, что это связано с тем, что LINQ запросы являются «ленивыми». Это точно?

Важным дополнительным является то, что они не «материализовались»/«в кэше» после выполнения, поэтому каждый раз, когда вы выполняете их, они восстанавливаются.

Эта линия

if (!q1.SequenceEqual(q2)) 
    Console.WriteLine("wow!"); 

вызовет оценку q1 и q2 перечислимых, что каждый из них будет вызывать перечисление ys. Таким образом, ys будет «сгенерирован» дважды для каждого цикла цикла .

Итак, учитывая, что цикл foreach будет делать 20000 «cyles», ys будет «сгенерирован» 40000 раз.

Random.Next() будет выполнено 20000 + (20000 * 2 * 5000) раз, где

20000: the xs sequence, used only once by the foreach cycle: 
20000 * 2 * 5000: 20000 cycles in which the ys sequence is used twice 

Обратите внимание, что произошло бы, если:

var xs = (from x in Enumerable.Range(0, 20000) 
     select generator.Next()).ToArray(); 

var ys = (from y in Enumerable.Range(0, 5000) 
     select generator.Next()).ToArray(); 

Здесь мы "материализуется" в перечислимых в массиве (точнее, в двух массивах). Random.Next() будет называться грандиозным итогом 20000 + 5000 раз, и это будет сделано непосредственно в этих двух строках. Перечисление xs и ys не приведет к генерации новых случайных чисел.

+0

Спасибо - это важная вещь, о которой нужно помнить. – rookie

+0

@rookie Я обновил ответ, добавив еще одну часть. – xanatos

+0

Да, спасибо. Это более или менее то, что я заметил при вызове 'ToList()' в запросах перед входом в цикл foreach. – rookie

Смежные вопросы