Из Википедии, бесплатной энциклопедии
Перейти к навигации Перейти к поиску

В программировании теории языка , ленивые вычислений , или вызов по необходимости , [1] является стратегией оценки , которая задерживает оценку из выражения , пока его значение не требуется ( нестрогая оценка ) и которая также позволяет избежать повторных оценок ( обмен ). [2] [3] Совместное использование может сократить время работы некоторых функций экспоненциально по сравнению с другими стратегиями нестрогого оценивания, такими как вызов по имени , которые многократно оценивают одну и ту же функцию вслепую, независимо от того, может ли функция быть memoized .

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

К преимуществам ленивого оценивания относятся:

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

Ленивое оценивание часто сочетается с запоминанием , как описано в книге Джона Бентли « Написание эффективных программ» . [4] После того, как значение функции вычислено для этого параметра или набора параметров, результат сохраняется в таблице поиска, которая индексируется значениями этих параметров; при следующем вызове функции выполняется консультация с таблицей, чтобы определить, доступен ли уже результат для этой комбинации значений параметров. Если да, то просто возвращается сохраненный результат. Если нет, функция оценивается, и в таблицу поиска добавляется еще одна запись для повторного использования.

Ленивая оценка может привести к сокращению объема памяти, поскольку значения создаются при необходимости. [5] Однако ленивое вычисление трудно сочетать с императивными функциями, такими как обработка исключений и ввод / вывод , потому что порядок операций становится неопределенным. Ленивое вычисление может привести к утечке памяти . [6] [7]

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

История [ править ]

Ленивое вычисление было введено для лямбда-исчисления Кристофером Уодсвортом [8] и использовано системой Plessey System 250 как критическая часть мета-машины лямбда-исчисления, уменьшая накладные расходы на разрешение для доступа к объектам в адресном пространстве с ограниченными возможностями. [9] Для языков программирования он был независимо введен Питером Хендерсоном и Джеймсом Х. Моррисом [10], а также Дэниелом П. Фридманом и Дэвидом С. Уайзом. [11] [12]

Приложения [ править ]

Отсроченная оценка используется, в частности, в функциональных языках программирования . При использовании отложенного вычисления выражение оценивается не сразу после его привязки к переменной, а тогда, когда оценщик вынужден произвести значение выражения. То есть, такой оператор, как x = expression;(то есть присвоение результата выражения переменной), явно требует, чтобы выражение было оценено и результат помещен в него x, но то, что на самом деле есть, не xимеет значения, пока не возникнет потребность в его значении. через ссылку на xв каком-то более позднем выражении, вычисление которого могло быть отложено, хотя в конечном итоге быстрорастущее дерево зависимостей будет обрезано, чтобы создать какой-то символ, а не другой, который будет видеть внешний мир. [13]

Отложенная оценка имеет преимущество в том, что она позволяет создавать вычисляемые бесконечные списки без бесконечных циклов или вопросов размера, влияющих на вычисления. Например, можно создать функцию, которая создает бесконечный список (часто называемый потоком ) чисел Фибоначчи . Вычисление n-го числа Фибоначчи было бы просто извлечением этого элемента из бесконечного списка, заставляя вычислять только первые n членов списка. [14] [15]

Например, в языке программирования Haskell список всех чисел Фибоначчи может быть записан как: [15]

 ФИБС  =  0  :  1  :  zipWith  ( + )  ФИБС  ( хвост  ФИБС )

В синтаксисе Haskell " :" добавляет элемент к списку, tailвозвращает список без его первого элемента и zipWithиспользует указанную функцию (в данном случае сложение) для объединения соответствующих элементов двух списков для создания третьего. [14]

Если программист проявляет осторожность, оцениваются только те значения, которые необходимы для получения определенного результата. Однако некоторые вычисления могут привести к тому, что программа попытается оценить бесконечное количество элементов; например, запрос длины списка или попытка суммирования элементов списка с помощью операции сворачивания приведет к тому, что программа либо не завершится, либо будет исчерпана память .

Структуры управления [ править ]

Практически во всех [ количественно ] распространенных «нетерпеливых» языках операторы if вычисляются лениво.

если а то б еще в

оценивает (a), тогда, если и только если (a) оценивается как true, он оценивает (b), в противном случае он оценивает (c). То есть ни (b), ни (c) не будут оцениваться. И наоборот, на нетерпеливом языке ожидаемое поведение таково, что

определить f (x, y) = 2 * xположим k = f (d, e)

будет по-прежнему оценивать (e) при вычислении значения f (d, e), даже если (e) не используется в функции f. Однако определяемые пользователем управляющие структуры зависят от точного синтаксиса, поэтому, например,

определим g (a, b, c) = if a then b else cl = g (h, i, j)

(i) и (j) оба будут оцениваться на нетерпеливом языке. На ленивом языке

l '= если h, то i else j

(i) или (j) будут оцениваться, но никогда оба.

Ленивое вычисление позволяет определять управляющие структуры обычным образом, а не как примитивы или методы времени компиляции. Если (i) или (j) имеют побочные эффекты или вносят ошибки времени выполнения, тонкие различия между (l) и (l ') могут быть сложными. Обычно можно ввести определяемые пользователем структуры ленивого управления в энергичных языках в качестве функций, хотя они могут отклоняться от синтаксиса языка для энергичной оценки: часто задействованные тела кода (например, (i) и (j)) должны быть заключены в значение функции, поэтому они выполняются только при вызове.

Вычисление короткого замыкания логических управляющих структур иногда называют ленивым .

Работа с бесконечными структурами данных [ править ]

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

numberFromInfiniteList  ::  Int  ->  Int numberFromInfiniteList  n  =  бесконечность  !!  n  -  1,  где  бесконечность  =  [ 1 .. ]main  =  print  $  numberFromInfiniteList  4

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

Шаблон списка успехов [ править ]

Избегайте чрезмерных усилий [ править ]

Составное выражение может быть в форме EasilyComputed или LotsOfWork, так что, если простая часть дает истинное значение, можно избежать большой работы. Например, предположим, что необходимо проверить большое число N, чтобы определить, является ли оно простым числом, и доступна ли функция IsPrime (N), но, увы, для ее оценки может потребоваться много вычислений. Возможно, N = 2 или [Mod (N, 2) ≠ 0 и IsPrime (N)] помогут, если будет много вычислений с произвольными значениями для N.

Предотвращение ошибок [ править ]

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

L: = длина (A); Пока L> 0 и A (L) = 0, делаем L: = L - 1;

Если все элементы массива равны нулю, цикл будет работать до L = 0, и в этом случае цикл должен быть завершен без попытки ссылаться на нулевой элемент массива, которого не существует.

Другое использование [ править ]

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

Другим примером лени в современных компьютерных системах является выделение страниц с копированием при записи или подкачка по запросу , когда память выделяется только при изменении значения, хранящегося в этой памяти. [16]

Лень может быть полезна в сценариях с высокой производительностью. Пример является Unix - ММАП функции, которая обеспечивает спрос загрузку страниц с диска, так что только те страницы , на самом деле коснулись загружаются в память, а ненужная память не выделяются.

MATLAB реализует копирование при редактировании , где массивы, которые копируются, имеют свое фактическое хранилище в памяти, реплицируемое только при изменении их содержимого, что может привести к ошибке нехватки памяти при последующем обновлении элемента, а не во время операции копирования. [17]

Реализация [ править ]

Некоторые языки программирования по умолчанию откладывают вычисление выражений, а некоторые другие предоставляют функции или специальный синтаксис для отсрочки вычисления. В Miranda и Haskell оценка аргументов функции по умолчанию откладывается. Во многих других языках оценка может быть отложена путем явной приостановки вычислений с использованием специального синтаксиса (как в схемах « delay» и « force» и OCaml « lazy» и « Lazy.force») или, в более общем смысле, путем помещения выражения в преобразователь . Объект, представляющий такую ​​явно отложенную оценку, называется ленивым будущим . Ракуиспользует ленивое вычисление списков, поэтому можно назначать бесконечные списки переменным и использовать их в качестве аргументов для функций, но в отличие от Haskell и Miranda, Raku по умолчанию не использует ленивое вычисление арифметических операторов и функций. [13]

Лень и рвение [ править ]

Сдерживание рвения в ленивых языках [ править ]

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

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

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

Кроме того, сопоставление с образцом в Haskell 98 по умолчанию строгое, поэтому ~нужно использовать квалификатор, чтобы сделать его ленивым. [18]

Имитация лени в нетерпеливых языках [ править ]

Java [ править ]

В Java ленивая оценка может выполняться с помощью объектов, у которых есть метод для их оценки, когда значение необходимо. Тело этого метода должно содержать код, необходимый для выполнения этой оценки. С момента появления лямбда-выражений в Java SE8 Java поддерживает для этого компактную нотацию. В следующем примере универсального интерфейса представлена ​​структура для ленивого вычисления: [19] [20]

интерфейс  Lazy < T >  {  T  eval (); }

LazyИнтерфейс с его eval()метода эквивалентен Supplierинтерфейсу с его get()методом в java.util.functionбиблиотеке. [21]

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

Ленивый < Целое число >  a  =  () ->  1 ; for  ( int  i  =  1 ;  i  <=  10 ;  i ++ )  {  final  Lazy < Integer >  b  =  a ;  а  =  () ->  б . eval ()  +  b . eval (); } Система . из . println (  "а ="  +  а. eval ()  );

В приведенном выше примере переменная a изначально относится к ленивому целочисленному объекту, созданному лямбда-выражением . Оценка этого лямбда-выражения эквивалентна созданию нового экземпляра анонимного класса, который реализуется с помощью метода eval, возвращающего 1 .()->1Lazy<Integer>

Каждая итерация цикла связывает a с новым объектом, созданным путем вычисления лямбда-выражения внутри цикла. Каждый из этих объектов содержит ссылку на другой ленивый объект, b , и имеет метод eval, который вызывается дважды и возвращает сумму. Переменная b здесь необходима, чтобы удовлетворить требование Java, чтобы переменные, на которые ссылается лямбда-выражение, были окончательными.b.eval()

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

Мы можем создать класс Java, который запоминает ленивые объекты следующим образом: [19] [20]

класс  Memo < T >  реализует  Lazy < T >  {  private  Lazy < T >  lazy ;  // ленивое выражение, eval устанавливает его равным нулю  private  T  memo  =  null ;  // меморандум предыдущего значения public  Memo (  Lazy < T >  lazy  )  {  // конструктор  this . ленивый  =  ленивый ;  } public  T  eval ()  {  if  ( lazy  ! =  null )  {  memo  =  lazy . eval ();  ленивый  =  ноль ;  }  напоминание о возврате  ; } } 

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

Ленивый < Целое число >  a  =  () ->  1 ; for  ( int  i  =  1 ;  i  <=  10 ;  i ++ )  {  final  Lazy < Integer >  b  =  a ;  a  =  новый  Memo < Integer > (  () ->  b . eval ()  +  b . eval ()  ); } Система .из . println (  "а ="  +  а . eval ()  );

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

JavaScript [ править ]

В JavaScript ленивую оценку можно смоделировать с помощью генератора . Например, поток всех чисел Фибоначчи можно записать, используя мемоизацию , как:

/ ** * Функции-генераторы возвращают объекты-генераторы, которые реализуют ленивое вычисление. * @return {! Generator <bigint>} Генератор целых чисел, отличный от нуля. * / function *  fibonacciNumbers ()  {  let  memo  =  [ 1n ,  - 1n ];  // создаем начальное состояние (например, вектор чисел "негафибоначчи")  while  ( true )  {  // бесконечно повторяем  memo  =  [ memo [ 0 ]  +  memo [ 1 ],  memo [ 0]]];  // обновляем состояние при каждой оценке  yield  memo [ 0 ];  // возвращаем следующее значение и приостанавливаем выполнение до возобновления  } }let  stream  =  fibonacciNumbers ();  // создаем поток чисел с отложенным вычислением let  first10  =  Array . from ( новый  массив ( 10 ),  ()  =>  stream . next (). value );  // оцениваем только первые 10 чисел console . журнал ( first10 );  // вывод [0n, 1n, 1n, 2n, 3n, 5n, 8n, 13n, 21n, 34n]

Python [ править ]

В Python 2.x range()функция [22] вычисляет список целых чисел. При оценке первого оператора присваивания весь список сохраняется в памяти, так что это пример активной или немедленной оценки:

>>> r  =  range ( 10 ) >>> print  r [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> print  r [ 3 ] 3

В Python 3.x range()функция [23] возвращает специальный объект диапазона, который вычисляет элементы списка по запросу. Элементы объекта диапазона генерируются только тогда, когда они необходимы (например, когда print(r[3])оценивается в следующем примере), так что это пример ленивой или отложенной оценки:

>>> r  =  range ( 10 ) >>> print ( r ) range (0, 10) >>> print ( r [ 3 ]) 3
Это изменение на ленивую оценку экономит время выполнения для больших диапазонов, которые могут никогда не использоваться полностью, и использование памяти для больших диапазонов, где в любой момент требуется только один или несколько элементов.

В Python 2.x можно использовать вызываемую функцию, xrange()которая возвращает объект, генерирующий числа в диапазоне по запросу. Преимущество xrangeзаключается в том, что сгенерированный объект всегда будет занимать один и тот же объем памяти.

>>> r  =  xrange ( 10 ) >>> print ( r ) xrange (10) >>> lst  =  [ x  для  x  в  r ] >>> print ( lst ) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Начиная с версии 2.2 Python демонстрирует ленивую оценку, реализуя итераторы (ленивые последовательности) в отличие от последовательностей кортежей или списков. Например (Python 2):

>>> числа  =  диапазон ( 10 ) >>> итератор  =  итер ( числа ) >>> напечатать  числа [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> напечатать  итератор < объект listiterator по адресу 0xf7e8dd4c> >>> итератор печати  . следующий () 0
В приведенном выше примере показано, что списки оцениваются при вызове, но в случае итератора первый элемент «0» печатается, когда возникает необходимость.

.NET Framework [ править ]

В .NET Framework с помощью класса можно выполнять ленивую оценку . [24] Класс может быть легко использован в F # с помощью ключевого слова, в то время как метод вызовет оценку. Существуют также специализированные коллекции, которые предоставляют встроенную поддержку ленивых вычислений. System.Lazy<T>lazyforceMicrosoft.FSharp.Collections.Seq

пусть  fibonacci  =  Seq . развернуть  ( fun  ( x ,  y )  ->  Some ( x ,  ( y ,  x  +  y )))  ( 0I , 1I ) fibonacci  |>  Seq . nth  1000

В C # и VB.NET класс используется напрямую. System.Lazy<T>

общественного  ИНТ  Sum () {  INT  = 0 ; int b = 0 ; Ленивый < int > x = новый Ленивый < int > (() => a + b ); а = 3 ; b = 5 ; вернуть x . Стоимость ; // возвращает 8 }                        

Или на более практическом примере:

// рекурсивное вычисление n-го числа Фибоначчи public  int  Fib ( int  n ) {  return  ( n  ==  1 )?  1  :  ( n  ==  2 )?  1  :  Fib ( n - 1 )  +  Fib ( n - 2 ); }public  void  Main () {  Console . WriteLine ( «Какое число Фибоначчи вы хотите вычислить?» );  int  n  =  Int32 . Разбор ( Консоль . ReadLine ());  Ленивый < int >  fib  =  new  Lazy < int > (()  =>  Fib ( n ));  // функция подготовлена, но не выполняется  bool  execute ;  если  ( n  >  100 ) {  Консоль . WriteLine ( «Это может занять некоторое время. Вы действительно хотите вычислить это большое число? [Y / n]» );  выполнить  =  ( Console . ReadLine ()  ==  "y" );  }  else  execute  =  true ;  if  ( выполнить )  Console . WriteLine ( фиб . Значение );  // число рассчитывается только при необходимости }

Другой способ - использовать yieldключевое слово:

// жадная оценка public  IEnumerable < int >  Fibonacci ( int  x ) {  IList < int >  fibs  =  new  List < int > (); int  prev  =  - 1 ;  int  next  =  1 ;  для  ( int  i  =  0 ;  i  <  x ;  i ++)  {  int  sum  =  prev  +  next ;  предыдущий  =  следующий ;  следующая  =  сумма ;  выдумки . Добавить ( сумма );  }  вернуть  выдумки ; }// ленивая оценка public  IEnumerable < int >  LazyFibonacci ( int  x ) {  int  prev  =  - 1 ;  int  next  =  1 ;  для  ( int  i  =  0 ;  i  <  x ;  i ++)  {  int  sum  =  prev  +  next ;  предыдущий  =  следующий ;  следующая  =  сумма ;  доходность  доходность сумма ;  } }

См. Также [ править ]

  • Комбинаторная логика
  • Каррирование
  • Поток данных
  • Нетерпеливые оценки
  • Функциональное программирование
  • Будущее и обещания
  • Генератор (компьютерное программирование)
  • Редукция графика
  • Инкрементальные вычисления - связанная концепция, согласно которой вычисления повторяются только при изменении их входных данных. Может сочетаться с ленивым вычислением.
  • Лямбда-исчисление
  • Ленивая инициализация
  • Смотреть вперед
  • Нестрогий язык программирования
  • Оценка нормального порядка
  • Оценка короткого замыкания (минимальная)

Ссылки [ править ]

  1. ^ Hudak 1989 , стр. 384
  2. ^ Дэвид Энтони Уотт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Вили и сыновья. С. 367–368. ISBN 978-0-470-85320-7. Проверено 30 декабря 2010 года .
  3. Перейти ↑ Reynolds 1998 , p. 307
  4. Бентли, Джон Луи. Написание эффективных программ. Прентис-Холл, 1985. ISBN 978-0139702440 
  5. Крис Смит (22 октября 2009 г.). Программирование на F # . O'Reilly Media, Inc. стр. 79. ISBN 978-0-596-15364-9. Проверено 31 декабря 2010 года .
  6. ^ Launchbury 1993 .
  7. ^ Эдвард З. Ян. «Зоопарк утечки космоса» .
  8. ^ Уодсворт 1971
  9. Хамер-Ходжес, Кеннет (1 января 2020 г.). Цивилизация киберпространства: борьба за цифровую демократию . п. 410. ISBN 978-1-95-163044-7. Проверено 29 февраля 2020 года .
  10. ^ Хендерсон и Моррис 1976
  11. Перейти ↑ Friedman & Wise, 1976
  12. Перейти ↑ Reynolds 1998 , p. 312
  13. ^ a b Филип Вадлер (2006). Функциональное и логическое программирование: 8-й международный симпозиум, FLOPS 2006, Фудзи-Сусоно, Япония, 24-26 апреля 2006 г .: материалы . Springer. п. 149. ISBN. 978-3-540-33438-5. Проверено 14 января 2011 года .
  14. ^ a b Даниэль Ле Метайер (2002). Языки и системы программирования: 11-й Европейский симпозиум по программированию, ESOP 2002, проведенный в рамках совместных европейских конференций по теории и практике программного обеспечения, ETAPS 2002, Гренобль, Франция, 8-12 апреля 2002 г .: материалы . Springer. С. 129–132. ISBN 978-3-540-43363-7. Проверено 14 января 2011 года .
  15. ^ a b Ассоциация вычислительной техники; Специальная группа ACM по языкам программирования (1 января 2002 г.). Материалы семинара ACM SIGPLAN Haskell 2002 (Haskell '02): Питтсбург, Пенсильвания, США; 3 октября 2002 . Ассоциация вычислительной техники. п. 40. ISBN 978-1-58113-605-0. Проверено 14 января 2011 года .
  16. ^ a b Ленивое и спекулятивное исполнение Батлер Лэмпсон Microsoft Research OPODIS, Бордо, Франция, 12 декабря 2006 г.
  17. ^ «Недостаточно памяти при присвоении значений существующим массивам? - Ответы MATLAB - Центр MATLAB» .
  18. ^ «Ленивое сопоставление с образцом - HaskellWiki» .
  19. ^ a b Гжегож Пивоварек, Использование лямбда-выражений для отложенного вычисления в Java , 4Comprehension , 25 июля 2018 г.
  20. ^ a b Дуглас У. Джонс, CS: 2820 Notes, осень 2020 г., лекция 25 , получено в январе 2021 г.
  21. ^ Interface Suppier <T> , получено в октябре 2020 г.
  22. ^ «2. Встроенные функции - документация Python 2.7.11» .
  23. ^ «2. Встроенные функции - документация Python 3.5.1» .
  24. ^ "Ленивый (T) класс (система)" . Microsoft.

Дальнейшее чтение [ править ]

  • Худак, Пол (сентябрь 1989 г.). «Концепция, развитие и применение языков функционального программирования». ACM Computing Surveys . 21 (3): 383–385. DOI : 10.1145 / 72551.72554 .
  • Рейнольдс, Джон С. (1998). Теории языков программирования . Издательство Кембриджского университета . ISBN 9780521594141. Проверено 23 февраля 2016 .
  • Хендерсон, Питер ; Моррис, Джеймс Х. (январь 1976 г.). «Ленивый оценщик» . Запись конференции третьего симпозиума ACM по принципам языков программирования .
  • Фридман, Д.П . ; Мудрый, Дэвид С. (1976). С. Майклсон; Р. Милнер (ред.). «Минусы не следует оценивать своими аргументами» (PDF) . Третий международный коллоквиум по языкам автоматов и программированию . Издательство Эдинбургского университета.
  • Лаанчбери, Джон (1993). «Естественная семантика ленивых оценок». Материалы 20-го симпозиума ACM SIGPLAN-SIGACT по принципам языков программирования (POPL '93) : 144–154. CiteSeerX  10.1.1.35.2016 . DOI : 10.1145 / 158511.158618 . ISBN 0897915607.

Внешние ссылки [ править ]

  • Макросы отложенного вычисления в Nemerle
  • Лямбда - исчисление в библиотеках буста в C ++ языка
  • Ленивая оценка в ANSI C ++ путем написания кода в стиле, который использует классы для реализации закрытия функций .