"Знай себе цену. Затем добавьте налог ». - Аноним

Одна из невысказанных поведенческих норм, навязанных обществом, заключается в том, что вы не должны откладывать то, что вы можете сделать сегодня. В отличие от «реального» мира, ожидание до самого последнего момента является довольно обычным явлением и даже поощряется в мире программирования (или, по крайней мере, в некоторых его областях). Причина? Это просто: не все того стоит.

Ленивый мир программирования

Концепция ленивой оценки в значительной степени основана на этой идее достоинства: если выражение слишком дорого и нет особой необходимости его оценивать, то не делай этого. Например, если вы рекурсивно определяете бесконечную структуру данных, каждый уровень рекурсии можно оценивать только по принципу вызов по необходимости.

Для иллюстрации вот фрагмент кода (написанный на псевдоязыке), который определяет бесконечный список точек и рекурсивно добавляет к нему квадрат числа:

В отличие от строгой оценки, data не будет оцениваться в ленивом мире (в конце концов, это бесконечный список, и при этом у вас может просто не хватить памяти!). Вместо этого вы можете оценить только нужные вам точки, написав что-нибудь в строке data[:5], что вернет первые пять элементов из списка. Преимущество заключается в повышении производительности вашей программы, поскольку вы избегаете ненужных вычислений.

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

Как это реализовано?

Чтобы ленивое вычисление действительно работало, необходимо добавить в язык специальное правило вычисления, и для этой цели был изобретен новый тип данных под названием thunk.

Преобразователь может находиться в двух состояниях:

(1) без оценки (значение выражения пока не использовалось, и, следовательно, мы не знаем, каково его значение), и

(2) оценено (требуется значение выражения - или, по крайней мере, некоторая его часть в случае бесконечных данных - следовательно, некоторые из его значений сохранены).

Класс Thunk отслеживает выражение и его среду (он должен это делать, потому что выражение потенциально может быть вычислено в какой-то другой среде).

В Python мы можем записать этот класс следующим образом (Исходный код ниже):

  • _evaluated: средство отслеживания того, было ли выражение вычислено или нет,
  • value: средство отслеживания значений после вычисления выражения,
  • forceeval: вызывать его, когда требуется значение выражения,
  • meval: вызывайте его, когда значение выражения не требуется (т.е. вы хотите отложить оценку).

Для поддержки отложенной оценки на языке должно быть выполнено следующее:

[…] Нам нужно переопределить evalApplication процедуру. Вместо того, чтобы оценивать все подвыражения операндов, новая процедура создает объекты Thunk, представляющие каждый операнд. Только первое подвыражение, то есть применяемая процедура, должно быть вычислено (Источник).

Дополнительная информация о мемоизации

Обычно концепция ленивого оценивания идет рука об руку с термином мемоизация - метод оптимизации хранения данных. Вот немного информации на тот случай, если вы наткнетесь на эту концепцию.

Проще говоря, мемоизация - это создание таблицы поиска, в которой сохраняются все оцененные значения. В следующий раз, когда вы вызовете конкретную функцию (которая уже вызывалась ранее), ей не нужно будет ничего повторно вычислять, если искомое значение уже существует в этой таблице поиска. Это экономит время вычислений и, не говоря уже, много памяти.

Итак, в случае бесконечного списка, определенного выше, если мы вызовем data[:5], пять значений будут сохранены в таблице поиска, и их не нужно будет пересчитывать в следующий раз, когда вы вызовете это выражение где-нибудь в вашем коде.

Не стесняйтесь обращаться к ссылкам, указанным ниже, для получения дополнительной информации, если это необходимо.

Использованная литература:





Https://www.cs.virginia.edu/~evans/cs150/book/ch13-laziness.pdf