Отложенное выполнение и нетерпеливая оценка - PullRequest
11 голосов
/ 25 марта 2010

Не могли бы вы дать мне пример для отложенного выполнения с нетерпением оценки в C #?

Я прочитал из MSDN, что отложенное выполнение в LINQ может быть реализовано либо с отложенной, либо с нетерпением оценкой. Я мог найти примеры в Интернете для отложенного выполнения с отложенной оценкой, однако я не смог найти никакого примера для отложенного выполнения с энергичной оценкой.

Кроме того, чем отложенное выполнение отличается от отложенной оценки? На мой взгляд, оба выглядят одинаково. Не могли бы вы привести пример?

Ответы [ 2 ]

44 голосов
/ 25 марта 2010

Вот мой ответ, но также обратите внимание, что Джон Скит говорил об этом сегодня в своем блоге и о том, что он не совсем согласен с MSDN, означающим «Ленивый», поскольку MSDN не совсем ясно, что именно означает «ленивый».когда они используют его в Насколько вы ленивы? его пост делает для интересного прочтения.

Дополнительно Википедия предполагает, что для ленивых вычислений должны быть соблюдены три правила ив MSDN не учитывается третья точка, так как выражение будет оцениваться более одного раза, если GetEnumerator вызывается снова (по спецификации Reset не реализован для объектов перечислителя, сгенерированных с использованием ключевого слова yield, и большинство linq используют его в настоящее время)


С учетом функции

int Computation(int index)

Немедленное выполнение

IEnumerable<int> GetComputation(int maxIndex)
{
    var result = new int[maxIndex];
    for(int i = 0; i < maxIndex; i++)
    {
        result[i] = Computation(i);
    }
    return result;
}
  • При вызове функции Computation выполняется maxIndex раз
  • GetEnumerator возвращает новый экземпляр перечислителя, больше ничего не делая.
  • Каждый вызов MoveNext помещает значение, сохраненное в следующем массиве.ячейка в Current члене IEnumerator и это все.

Стоимость : большой аванс, маленький при перечислении (только копия)

Отложенное, но нетерпеливое выполнение

IEnumerable<int> GetComputation(int maxIndex)
{
    var result = new int[maxIndex];
    for(int i = 0; i < maxIndex; i++)
    {
        result[i] = Computation(i);
    }
    foreach(var value in result)
    {
        yield return value;
    }
}
  • Когда вызывается функция, создается экземпляр автоматически сгенерированного класса (в спецификации называемого "перечислимым объектом"), реализующего IEnumerable, и создается копия аргумента.(maxIndex) хранится в нем.
  • GetEnumerator возвращает новый экземпляр перечислителя, ничего больше не делая.
  • Первый вызов MoveNext выполняет maxIndex, умноженный на метод вычисления,сохраните результат в массиве и Current вернет первое значение.
  • Каждый последующий вызов MoveNext приведет к Current значению, сохраненному в массиве.

Стоимость : ничего заранее, Большой, когда начинается перечисление, Маленький во время перечисления (только копия)

Отложенное и ленивое выполнение

IEnumerable<int> GetComputation(int maxIndex)
{
    for(int i = 0; i < maxIndex; i++)
    {
        yield return Computation(i);
    }
}
  • КогдаФункция вызывается так же, как и случай с отложенным выполнением.
  • GetEnumerator возвращает новый экземпляр перечислителя, больше ничего не делая.
  • Каждый вызов MoveNext выполняется один раз с кодом Computation, помещает значение в Current и немедленно разрешает вызывающей сторонедействовать на результат.

Большинство linq используют отложенное и ленивое выполнение, но некоторые функции не могут быть похожи на сортировку.

Стоимость : ничего предоплата,Умеренный во время перечисления (вычисления выполняются там)

Подводя итог

  • Немедленно означает, что вычисление / выполнение выполняется в функции и завершается после ее возврата.(Полностью рвение оценка, как это делает большинство кодов C #)
  • Отложено / Стремление означает, что большая часть работы будет выполнена в первый MoveNext или когда IEnumerator экземпляр создан (для IEnumerable это когда GetEnumerator вызывается)
  • Отложено / Ленивый означает, что работа будет выполняться каждый раз, когда вызывается MoveNext, но ничегоbefore.

Parallel LINQ делает это немного по-другому, поскольку вычисление можно считать отложенным / ленивым с точки зрения вызывающей стороны, но внутренне вычисление некоторого числа элементовначать параллельно, как только начнется перечисление.В результате, если следующее значение уже есть, вы получите его немедленно, но в противном случае вам придется его ждать.

1 голос
/ 25 марта 2010

Один из способов, которым вы можете с нетерпением оценить отложенное выполнение IEnumerable, - просто превратить его в массив с помощью функции linq .ToArray ().

var evaluated = enumerable.ToArray();

Это вызывает оценку полного перечислимого, и тогда у вас есть массив, с которым вы можете делать все, что захотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...