stringtranslate.com

Стратегия оценки

В языке программирования стратегия оценки — это набор правил для оценки выражений. [1] Этот термин часто используется для обозначения более конкретного понятия стратегии передачи параметров [2] , которая определяет вид значения, передаваемого функции для каждого параметра ( стратегия связывания ) [3] и следует ли оценивать параметры вызова функции, и если да, то в каком порядке ( порядок оценки ). [4] Понятие стратегии сокращения является отдельным, [5] хотя некоторые авторы объединяют эти два термина, и определение каждого термина не является общепринятым. [6]

Для иллюстрации, выполнение вызова функции f(a,b)может сначала оценить аргументы aи b, сохранить результаты в ссылках или ячейках памяти ref_aи ref_b, а затем оценить тело функции с этими переданными ссылками. Это дает функции возможность искать исходные значения аргументов, переданные через разыменование параметров (некоторые языки используют специальные операторы для этого), изменять их через присваивание , как если бы они были локальными переменными, и возвращать значения через ссылки. Это стратегия оценки вызова по ссылке. [7]

Стратегия оценки является частью семантики определения языка программирования. Некоторые языки, такие как PureScript , имеют варианты с различными стратегиями оценки. Некоторые декларативные языки , такие как Datalog , поддерживают несколько стратегий оценки. Некоторые языки определяют соглашение о вызовах . [ необходимо разъяснение ]

Стол

Это таблица стратегий оценки и репрезентативных языков по году введения. Репрезентативные языки перечислены в хронологическом порядке, начиная с языка(ов), которые ввели стратегию, а затем следуют известные языки, которые используют стратегию. [8] : 434 

Оценочные заказы

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

def  f ( x ):  print ( x ,  end = '' )  return  xраспечатать ( f ( 1 )  +  f ( 2 ), конец = '' )

выводит данные 123из-за порядка вычисления слева направо в Python, но похожая программа на OCaml :

пусть  f  x  =  print_int  x ;  x  ;; print_int  ( f  1  +  f  2 )

выходные данные 213из-за порядка оценки OCaml справа налево.

Порядок оценки в основном виден в коде с побочными эффектами , но он также влияет на производительность кода, поскольку жесткий порядок препятствует планированию инструкций . По этой причине стандарты языка, такие как C++, традиционно оставляют порядок неопределенным, хотя такие языки, как Java и C#, определяют порядок оценки как слева направо [8] : 240–241  , а стандарт C++17 добавил ограничения на порядок оценки. [21]

Строгая оценка

Аппликативный порядок — это семейство порядков оценки, в которых аргументы функции полностью оцениваются до того, как функция будет применена. [22] Это приводит к тому, что функция становится строгой , т. е. результат функции не определен, если какой-либо из аргументов не определен, поэтому аппликативный порядок оценки чаще называют строгой оценкой . Кроме того, вызов функции выполняется, как только он встречается в процедуре, поэтому его также называют жадной оценкой или жадной оценкой . [23] [24] Некоторые авторы называют строгую оценку «вызовом по значению» из-за стратегии связывания вызова по значению, требующей строгой оценки. [4]

Common Lisp, Eiffel и Java оценивают аргументы функций слева направо. C оставляет порядок неопределенным. [25] Scheme требует, чтобы порядок выполнения был последовательным выполнением неопределенной перестановки аргументов. [26] OCaml аналогичным образом оставляет порядок неопределенным, но на практике оценивает аргументы справа налево из-за конструкции своей абстрактной машины . [27] Все они являются строгими вычислениями.

Нестрогая оценка

Нестрогий порядок оценки — это порядок оценки, который не является строгим, то есть функция может вернуть результат до того, как все ее аргументы будут полностью оценены. [28] : 46–47  Прототипическим примером является нормальный порядок оценки , который не оценивает ни один из аргументов, пока они не понадобятся в теле функции. [29] Нормальный порядок оценки имеет свойство завершаться без ошибок всякий раз, когда любой другой порядок оценки завершился бы без ошибок. [30] Название «нормальный порядок» происходит от лямбда-исчисления, где редукция нормального порядка найдет нормальную форму, если таковая имеется (это «нормализирующая» стратегия редукции ). [31] Ленивая оценка классифицируется в этой статье как метод связывания, а не порядок оценки. Но это различие не всегда соблюдается, и некоторые авторы определяют ленивую оценку как оценку нормального порядка или наоборот, [22] [32] или путают нестрогость с ленивой оценкой. [28] : 43–44 

Булевы выражения во многих языках используют форму нестрогой оценки, называемую короткой оценкой , где оценка оценивает левое выражение, но может пропустить правое выражение, если результат может быть определен, например, в дизъюнктивном выражении (ИЛИ), где trueвстречается, или в конъюнктивном выражении (И), где falseвстречается, и т. д. [32] Условные выражения аналогичным образом используют нестрогую оценку — оценивается только одна из ветвей. [28]

Сравнение оценки аппликационного и нормального порядка

При оценке нормального порядка выражения, содержащие дорогостоящие вычисления, ошибки или бесконечные циклы, будут игнорироваться, если они не нужны, [4] позволяя специфицировать определяемые пользователем конструкции потока управления, что недоступно при оценке аппликационного порядка. Оценка нормального порядка использует сложные структуры, такие как переходники для неоцененных выражений, по сравнению со стеком вызовов , используемым при оценке аппликационного порядка. [33] Оценка нормального порядка исторически имела недостаток пригодных для использования инструментов отладки из-за своей сложности. [34]

Строгие обязательные стратегии

Звонок по значению

При вызове по значению (или передаче по значению) вычисленное значение выражения аргумента привязывается к соответствующей переменной в функции (часто путем копирования значения в новую область памяти). Если функция или процедура может присваивать значения своим параметрам, присваивается только ее локальная переменная — то есть все, что передается в вызов функции, остается неизменным в области действия вызывающей стороны , когда функция возвращается. Например, в Pascal передача массива по значению приведет к копированию всего массива, и любые изменения в этом массиве будут невидимы для вызывающей стороны: [35]

программа Main ; использует crt ;  procedure PrintArray ( a : Массив целых чисел ) ; var i : Целое число ; begin for i := Low ( a ) to High ( a ) do Write ( a [ i ]) ; WriteLn () ; end ;               Процедура Modify ( Строка : Массив целых чисел ) ; начало PrintArray ( Строка ) ; // 123 Строка [ 1 ] := 4 ; PrintArray ( Строка ) ; // 143 конец ;              Var A : Массив целых чисел ; begin A := [ 1 , 2 , 3 ] ; PrintArray ( A ) ; // 123 Modify ( A ) ; PrintArray ( A ) ; // 123 end .              

Семантический дрейф

Строго говоря, при вызове по значению никакие операции, выполняемые вызываемой подпрограммой, не могут быть видны вызывающему, кроме как как часть возвращаемого значения. [16] Это подразумевает форму чисто функционального программирования в семантике реализации. Однако иносказание «вызов по значению, где значение является ссылкой» стало обычным в некоторых языках, например, в сообществе Java. [36] По сравнению с традиционной передачей по значению, передаваемое значение не является значением, как оно понимается в обычном значении значения, например, целым числом, которое может быть записано как литерал, а внутренним дескриптором ссылки реализации . Мутации этого дескриптора ссылки видны в вызывающем. Из-за видимой мутации эта форма «вызова по значению» более правильно называется вызовом путем разделения. [16]

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

Звонок по ссылке

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

Из-за различий в синтаксисе разница между вызовом по ссылке (где тип ссылки неявный) и вызовом по разделению (где тип ссылки явный) часто неясна на первый взгляд. Простая лакмусовая бумажка — возможно ли написать традиционную swap(a, b)функцию на этом языке. [36] Например, в Fortran:

программа Main неявный none целое :: a = 1 целое :: b = 2 вызов Swap ( a , b ) печать * , a , b !2 1 содержит  подпрограмму Swap ( a , b ) целое , намерение ( inout ) :: a , b целое :: temp temp = a a = b b = temp конец подпрограммы Swap конец программы Main                                     

Таким образом, намерение Fortran inoutреализует вызов по ссылке; любая переменная может быть неявно преобразована в ссылочный дескриптор. В отличие от этого, самое близкое, что можно получить в Java, это:

class  Main { static class Box { int value ; public Box ( int value ) { this.value = value ; } } static void swap ( Box a , Box b ) { int temp = a .value ; a .value = b .value ; b .value = temp ; } public static void main ( String [] args ) { Box a = new Box ( 1 ); Box b = new Box ( 2 ) ; swap ( a , b ) ; System .out .println ( String .format ( " % d %d " , a .value , b .value ) ) ; } } // вывод : 2 1                                                        

где для введения дескриптора должен использоваться явный Boxтип. Java — это вызов по разделению, но не вызов по ссылке. [36]

Вызов копированием-восстановлением

Вызов копированием-восстановлением — также известный как «копирование-ввод-вывод», «вызов по значению результата», «вызов по значению возврата» (как это называется в сообществе Fortran ) — это разновидность вызова по ссылке. При вызове копированием-восстановлением содержимое аргумента копируется в новую переменную, локальную для вызова вызова. Затем функция может изменить эту переменную, аналогично вызову по ссылке, но поскольку переменная является локальной, изменения не видны за пределами вызова вызова во время вызова. Когда вызов функции возвращается, обновленное содержимое этой переменной копируется обратно, чтобы перезаписать исходный аргумент («восстановлено»). [37]

Семантика вызова по копированию-восстановлению во многих случаях похожа на вызов по ссылке, но отличается, когда два или более аргументов функции являются псевдонимами друг друга (т. е. указывают на одну и ту же переменную в среде вызывающей стороны). При вызове по ссылке запись в один аргумент повлияет на другой во время выполнения функции. При вызове по копированию-восстановлению запись в один аргумент не повлияет на другой во время выполнения функции, но в конце вызова значения двух аргументов могут различаться, и неясно, какой аргумент копируется обратно первым и, следовательно, какое значение получает переменная вызывающей стороны. [38] Например, Ada указывает, что назначение копирования для каждого параметра in outили outпроисходит в произвольном порядке. [39] Из следующей программы (незаконной в Ada 2012) [40] можно увидеть, что поведение GNAT заключается в копировании слева направо при возврате:

с  Ada.Text_IO ;  использовать  Ada.Text_IO ;procedure  Test_Copy_Restore  is  procedure  Modify  ( A ,  B  : in  out  Integer )  is  begin  A  :=  A  +  1 ;  B  :=  B  +  2 ;  end  Modify ;  X  :  Integer  :=  0 ; begin  Modify ( X ,  X );  Put_Line ( "X = "  &  Integer ' Image ( X )); end  Test_Copy_Restore ; -- $ gnatmake -gnatd.E test_copy_restore.adb; ./test_copy_restore -- test_copy_restore.adb:12:10: предупреждение: доступный для записи фактический для "A" перекрывается с фактическим для "B" [-gnatw.i] -- X = 2

Если бы программа вернула 1, это было бы копирование справа налево, а при вызове по семантике ссылки программа вернула бы 3.

Когда ссылка передается вызывающей стороне неинициализированной (например, outпараметр в Ada, а не in outпараметр), такую ​​стратегию оценки можно назвать «вызовом по результату».

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

Звонок путем обмена

Вызов по разделению (также известный как «передача по разделению», «вызов по объекту» или «вызов по разделению объекта») — это стратегия оценки, которая является промежуточной между вызовом по значению и вызовом по ссылке. Вместо того, чтобы каждая переменная была представлена ​​как ссылка, только определенный класс значений, называемых «ссылками», « упакованными типами » или «объектами», имеет семантику ссылок, и именно адреса этих указателей передаются в функцию. Как и в случае вызова по значению, значение переданного адреса является копией, а прямое назначение параметру функции перезаписывает копию и не видно вызывающей функции. Как и в случае вызова по ссылке, изменение цели указателя видно вызывающей функции. Мутации изменяемого объекта внутри функции видны вызывающей функции, потому что объект не копируется и не клонируется — он является общим , отсюда и название «вызов по разделению». [16]

Впервые этот метод был описан Барбарой Лисков в 1974 году для языка CLU . [16] Он используется во многих современных языках, таких как Python (общие значения называются «объектами»), [42] Java (объекты), Ruby (объекты), JavaScript (объекты), Scheme (структуры данных, такие как векторы), [43] AppleScript (списки, записи, даты и объекты скриптов), OCaml и ML (ссылки, записи, массивы, объекты и другие составные типы данных), Maple (rtables и таблицы) и Tcl (объекты). [44] Термин «вызов путем совместного использования», используемый в этой статье, не является общепринятым; терминология противоречива в разных источниках. Например, в сообществе Java говорят, что Java — это вызов по значению. [36]

Для неизменяемых объектов нет никакой реальной разницы между вызовом по разделению и вызовом по значению, за исключением случаев, когда идентичность объекта видна в языке. Использование вызова по разделению с изменяемыми объектами является альтернативой параметрам ввода/вывода : параметр не назначается (аргумент не перезаписывается и идентичность объекта не изменяется), но объект (аргумент) мутирует. [45]

Например, в Python списки изменяемы и передаются с помощью вызова путем совместного использования, поэтому:

def  f ( a_list ) :  a_list .append ( 1 )м  =  [] ф ( м ) печать ( м )

выводит, [1]потому что appendметод изменяет объект, для которого он вызван.

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

def  f ( a_list ):  a_list  =  a_list  +  [ 1 ]  print ( a_list )  # [1]м  =  [] ф ( м ) печать ( м )  # []

Звонок по адресу

Вызов по адресу , передача по адресу или вызов/передача по указателю — это метод передачи параметров, в котором адрес аргумента передается как формальный параметр. Внутри функции адрес (указатель) может использоваться для доступа к значению аргумента или его изменения. Например, операция обмена может быть реализована следующим образом в C: [46]

#include <stdio.h> void swap ( int * a , int * b ) { int temp = * a ; * a = * b ; * b = temp ; }               int main () { int a = 1 ; int b = 2 ; swap ( &a a , & b ); printf ( "%d %d" , a , b ); // 2 1 return 0 ; }                  

Некоторые авторы рассматривают &как часть синтаксиса вызова swap. Согласно этой точке зрения, C поддерживает стратегию передачи параметров по ссылке. [47] Другие авторы придерживаются иной точки зрения, что представленная реализация swapв C является лишь имитацией вызова по ссылке с использованием указателей. [48] Согласно этой «симуляционной» точке зрения, изменяемые переменные в C не являются первоклассными (то есть l-значения не являются выражениями), а скорее являются типами указателей. Согласно этой точке зрения, представленная программа подкачки является синтаксическим сахаром для программы, которая использует указатели повсюду, [49] например, эта программа ( readи assignбыли добавлены, чтобы подчеркнуть сходство с Boxпрограммой вызова по разделению Java выше):

#include <stdio.h> int read ( int * p ) { return * p ; }     void присваивать ( int * p , int v ) { * p = v ; }        void swap ( int * a , int * b ) { int temp_storage ; int * temp = & temp_storage ; назначить ( temp , read ( a )); назначить ( a , read ( b )); назначить ( b , read ( temp )); }                 int main () { int a_storage ; int * a = & a_storage ; int b_storage ; int * b = & b_storage ; назначить ( a , 1 ); назначить ( b , 2 ); поменять местами ( a , b ); printf ( "%d %d" , прочитать ( a ), прочитать ( b )); // 2 1 вернуть 0 ; }                        

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

C++ еще больше запутывает проблему, позволяя swapобъявлять и использовать очень легкий «ссылочный» синтаксис: [50]

void swap ( int & a , int & b ) { int temp = a ; a = b ; b = temp ; }               int main () { int a = 1 ; int b = 2 ; swap ( a , b ); std :: cout << a << b << std :: endl ; // 2 1 return 0 ; }                      

Семантически это эквивалентно примерам на языке C. Поэтому многие авторы считают вызов по адресу уникальной стратегией передачи параметров, отличной от вызова по значению, вызова по ссылке и вызова по совместному использованию.

Призыв к объединению

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

Стратегии нестрогого связывания

Назвать по имени

Вызов по имени — это стратегия оценки, при которой аргументы функции не оцениваются до вызова функции, а подставляются непосредственно в тело функции (используя подстановку, избегающую захвата ), а затем остаются для оценки всякий раз, когда они появляются в функции. Если аргумент не используется в теле функции, он никогда не оценивается; если он используется несколько раз, он переоценивается каждый раз, когда появляется. (См. устройство Дженсена для техники программирования, которая использует это.)

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

Языки .NET могут имитировать вызов по имени с использованием делегатов или Expression<T>параметров. Последнее приводит к абстрактному синтаксическому дереву , заданному для функции. Eiffel предоставляет агенты, которые представляют операцию, которая должна быть оценена при необходимости. Seed7 предоставляет вызов по имени с параметрами функции. Программы Java могут выполнять аналогичную ленивую оценку с использованием лямбда-выражений и java.util.function.Supplier<T>интерфейса.

Звонок по необходимости

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

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

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

Ленивая оценка — наиболее распространенная реализация семантики вызова по потребности, но существуют и такие вариации, как оптимистическая оценка. Языки .NET реализуют вызов по потребности с помощью типа Lazy<T>.

Редукция графа — это эффективная реализация ленивых вычислений.

Вызов по макрорасширению

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

Звонок по будущему

"Call by future", также известный как "parallel call by name" или "lenient evaluation", [51] - это стратегия параллельной оценки, сочетающая нестрогую семантику с нетерпеливой оценкой. Метод требует мелкозернистого динамического планирования и синхронизации, но подходит для машин с массовым параллелизмом.

Стратегия создает будущее (обещание) для тела функции и каждого из ее аргументов. Эти будущие события вычисляются одновременно с потоком остальной части программы. Когда будущее A требует значения другого будущего B, которое еще не было вычислено, будущее A блокируется до тех пор, пока будущее B не завершит вычисления и не получит значение. Если будущее B уже завершило вычисления, значение возвращается немедленно. Условные выражения блокируются до тех пор, пока их условие не будет оценено, а лямбды не создают будущие события, пока они не будут полностью применены. [52]

Если реализовано с процессами или потоками, создание будущего создаст один или несколько новых процессов или потоков (для обещаний), доступ к значению синхронизирует их с основным потоком, а завершение вычисления будущего соответствует остановке обещаний, вычисляющих его значение. Если реализовано с помощью сопрограммы , как в .NET async/await , создание будущего вызывает сопрограмму (асинхронную функцию), которая может уступить вызывающему и, в свою очередь, быть уступлена обратно, когда значение используется, кооперативно выполняя многозадачность.

Стратегия недетерминирована, так как оценка может произойти в любой момент между созданием будущего (т. е. когда выражение задано) и использованием значения будущего. Стратегия нестрогая, так как тело функции может возвращать значение до оценки аргументов. Однако в большинстве реализаций выполнение все равно может застрять при оценке ненужного аргумента. Например, программа

f x = 1 / x g y = 1 main = print ( g ( f 0 ))           

может либо gзакончиться до f, и вывести 1, либо может привести к ошибке из-за оценки 1/0. [28]

Call-by-future похож на call-by-need в том, что значения вычисляются только один раз. При тщательной обработке ошибок и незавершенности, в частности, при завершении futures на полпути, если определено, что они не понадобятся, call-by-future также имеет те же свойства завершения, что и оценка call-by-need. [52] Однако call-by-future может выполнять ненужную спекулятивную работу по сравнению с call-by-need, например, глубокую оценку ленивой структуры данных. [28] Этого можно избежать, используя ленивые futures, которые не начинают вычисление, пока не будет уверенности, что значение необходимо.

Оптимистическая оценка

Оптимистическая оценка — это вариант вызова по необходимости, где аргумент функции частично оценивается в стиле вызова по значению в течение некоторого времени (которое может быть скорректировано во время выполнения ). По истечении этого времени оценка прерывается, и функция применяется с использованием вызова по необходимости. [53] Такой подход позволяет избежать некоторых затрат времени выполнения вызова по необходимости, сохраняя при этом желаемые характеристики завершения.

Смотрите также

Ссылки

  1. ^ Араки, Шота; Нишизаки, Син-я (ноябрь 2014 г.). «Вычисление по имени RPC и RMI исчислений». Теория и практика вычислений. стр. 1. doi :10.1142/9789814612883_0001. ISBN 978-981-4612-87-6. Получено 21 августа 2021 г. .
  2. ^ Турбак, Франклин; Гиффорд, Дэвид (18 июля 2008 г.). Концепции дизайна в языках программирования. MIT Press. стр. 309. ISBN 978-0-262-30315-6.
  3. ^ Crank, Erik; Felleisen, Matthias (1991). "Передача параметров и лямбда-исчисление". Труды 18-го симпозиума ACM SIGPLAN-SIGACT по принципам языков программирования - POPL '91 . стр. 2. CiteSeerX 10.1.1.23.4385 . doi :10.1145/99583.99616. ISBN  0897914198. S2CID  5782416.
  4. ^ abc Вильгельм, Рейнхард; Зайдль, Гельмут (10 ноября 2010 г.). Проектирование компилятора: виртуальные машины. Springer Science & Business Media. п. 61. ИСБН 978-3-642-14909-2.
  5. ^ Нита, Стефания Лоредана; Михайлеску, Мариус (2017). "Введение". Практический параллельный Haskell . п. 3. дои : 10.1007/978-1-4842-2781-7_1. ISBN 978-1-4842-2780-0.
  6. ^ Пирс, Бенджамин С. (2002). Типы и языки программирования. MIT Press . стр. 56. ISBN 0-262-16209-1.
  7. ^ Дэниел П. Фридман; Митчелл Ванд (2008). Основы языков программирования (третье изд.). Кембридж, Массачусетс: The MIT Press . ISBN 978-0262062794.
  8. ^ ab Скотт, Майкл Ли (2016). Прагматика языка программирования (Четвертое изд.). Уолтем, Массачусетс: Elsevier. ISBN 9780124104778.
  9. ^ «Избегайте ненужных копий данных — MATLAB и Simulink». www.mathworks.com . Получено 28.01.2023 .
  10. ^ Хасти, Ребекка. «Передача параметров». CS 536: Введение в языки программирования и компиляторы . Университет Висконсина . Получено 22 августа 2021 г.
  11. ^ JA Robinson (январь 1965). «Машинно-ориентированная логика, основанная на принципе разрешения». Журнал ACM . 12 (1): 23–41. doi : 10.1145/321250.321253 . S2CID  14389185.; Здесь: раздел 5.8, стр.32
  12. ^ JA Robinson (1971). «Вычислительная логика: унификация вычислений». Machine Intelligence . 6 : 63–72.
  13. ^ Банди, Алан; Уоллен, Линкольн (1984). "SASL". Каталог инструментов искусственного интеллекта . стр. 117. doi :10.1007/978-3-642-96868-6_222. ISBN 978-3-540-13938-6. Вероятно, это был первый язык, который систематически использовал возможности ленивых вычислений.
  14. ^ Фэй, Колин (30 июля 2018 г.). «О ленивой оценке». R-блоггеры . Получено 21 августа 2021 г.
  15. ^ Уодсворт, Кристофер П. (1971). Семантика и прагматика лямбда-исчисления (PhD). Оксфордский университет.
  16. ^ abcde Лисков, Барбара; Аткинсон, Расс; Блум, Тоби; Мосс, Элиот; Шафферт, Крейг; Шейфлер, Крейг; Снайдер, Алан (октябрь 1979 г.). "CLU Reference Manual" (PDF) . Лаборатория компьютерных наук . Массачусетский технологический институт. стр. 14–15. Архивировано (PDF) из оригинала 22-09-2006 . Получено 19-05-2011 .
  17. ^ "PHP: Передача по ссылке - Руководство". www.php.net . Получено 2021-07-04 .
  18. ^ Вагнер, Билл (12 апреля 2023 г.). «Передача параметров — Руководство по программированию на C#». Microsoft Docs . Получено 10 сентября 2023 г.
  19. ^ Доллард, Кэтлин (15 сентября 2021 г.). «Передача аргументов по значению и по ссылке — Visual Basic». Microsoft Docs . Получено 10 сентября 2023 г.
  20. ^ ab "История C++". en.cppreference.com . Получено 11 июня 2022 г. .
  21. ^ Филипек, Бартломей (16 августа 2021 г.). «Более строгий порядок оценки выражений в C++17». Истории C++ . Получено 24 августа 2021 г.
  22. ^ ab Абельсон, Гарольд ; Сассман, Джеральд Джей (1996). "Нормальный порядок и аппликативный порядок". Структура и интерпретация компьютерных программ (2-е изд.). Кембридж, Массачусетс: MIT Press . ISBN 0-262-01153-0. Архивировано из оригинала 2005-03-02 . Получено 2006-03-06 .См. также сноску Temp 576.
  23. ^ Риз, Ричард М. (14 октября 2015 г.). Изучение функционального программирования Java. Packt Publishing Ltd. стр. 106. ISBN 978-1-78528-935-4.
  24. ^ Антани, Вед; Тиммс, Саймон; Мантила, Дэн (31 августа 2016 г.). JavaScript: Функциональное программирование для разработчиков JavaScript. Packt Publishing Ltd. стр. 614. ISBN 978-1-78712-557-5.
  25. ^ Seacord, Robert C. "EXP30-C. Не полагайтесь на порядок оценки побочных эффектов". Стандарт кодирования SEI CERT C. Университет Карнеги-Меллона . Получено 23 августа 2021 г.
  26. ^ Anglade, S.; Lacrampe, JJ; Queinnec, C. (октябрь 1994 г.). «Семантика комбинаций в схеме» (PDF) . Указатели ACM SIGPLAN Lisp . VII (4): 15–20. doi :10.1145/382109.382669. S2CID  2987427.
  27. ^ «Почему аргументы функций OCaml вычисляются справа налево?». OCaml . 30 ноября 2017 г.
  28. ^ abcde Tremblay, G. (апрель 2000 г.). «Снисходительная оценка не является ни строгой, ни ленивой». Computer Languages ​​. 26 (1): 43–66. CiteSeerX 10.1.1.137.9885 . doi :10.1016/S0096-0551(01)00006-6. 
  29. ^ Джордж, Лай (март 1987). Эффективная оценка нормального порядка через информацию о строгости (магистр наук). Университет Юты. стр. 10.
  30. ^ Борнинг, Алан (осень 1999 г.). «Аппликативная и нормальная оценка порядка в функциональных языках» (PDF) . CSE 505: Концепции языков программирования . Вашингтонский университет . Получено 23 августа 2021 г. .
  31. ^ Mazzola, Guerino; Milmeister, Gérard; Weissmann, Jody (21 октября 2004 г.). Comprehensive Mathematics for Computer Scientists 2. Springer Science & Business Media. стр. 323. ISBN 978-3-540-20861-7.
  32. ^ ab Sturm, Oliver (11 апреля 2011 г.). Функциональное программирование на C#: классические методы программирования для современных проектов. John Wiley and Sons. стр. 91. ISBN 978-0-470-74458-1.
  33. ^ Марлоу, Саймон. «Почему я не могу получить трассировку стека?». Haskell Implementors Workshop 2012. Получено 25 августа 2021 г.
  34. ^ Нильссон, Хенрик (1999). «Трассировка по частям: доступная отладка для ленивых функциональных языков». Труды четвертой международной конференции ACM SIGPLAN по функциональному программированию . стр. 36–47. CiteSeerX 10.1.1.451.6513 . doi :10.1145/317636.317782. ISBN  1581131119. S2CID  13954359.
  35. ^ "Параметры открытого массива". www.freepascal.org . Получено 20 января 2024 г. .
  36. ^ abcd "Java is Pass-by-Value, Dammit!". 16 мая 2001 г. Получено 24 декабря 2016 г.
  37. ^ Коэнен, Франс. «ПЕРЕДАЧА ПАРАМЕТРОВ». cgi.csc.liv.ac.uk. ​Проверено 22 января 2024 г.
  38. ^ "Вызов по ссылке, проблемы псевдонимов" (PDF) . Курс MPRI 2-36-1: Доказательство программы (лекционные заметки) . стр. 53.
  39. ^ Справочное руководство по языку Ada 2022 (PDF) . 13 октября 2023 г. стр. 215.
  40. ^ Барнс, Джон (2013). Ada 2012 reasone: the language, the standard libraries (PDF) . Гейдельберг: Springer. стр. 15-16,87-88. ISBN 978-3-642-45210-9.
  41. ^ Терлоу, Роберт (май 2009 г.). "RPC: спецификация протокола удаленного вызова процедур версии 2". tools.ietf.org . IETF . Получено 7 апреля 2018 г. .
  42. ^ Лунд, Фредрик. "Вызов по объекту". Effbot.org . Архивировано из оригинала 2011-05-19 . Получено 2011-05-19 .
  43. ^ Джонс, Рис Прайс (2010). «Является ли Scheme вызовом по значению?». CS 145 Programming Languages ​​Lab 9: Parameter Passing . Университет Джорджа Вашингтона. Архивировано из оригинала 16 октября 2014 года . Получено 20 января 2024 года .
  44. ^ "Процедуры библиотеки Tcl - страница руководства Tcl_Obj". www.tcl.tk .
  45. ^ "CA1021: Избегайте выходных параметров". Microsoft. 15 ноября 2016 г.
  46. Лео, Рэй (ноябрь 1996 г.). Little C++ (Made Easy). LeoSudo Inc., стр. 79–80. ISBN 978-0-9654634-1-6.
  47. ^ Дандамуди, Сиварама П. (15 июля 2005 г.). Руководство по программированию на языке ассемблера в Linux. Springer Science & Business Media. стр. 232. ISBN 978-0-387-25897-3.
  48. ^ Шривастава, SK; Шривастава, Дипали (6 июня 2018 г.). C in Depth. BPB Publications. стр. 206. ISBN 978-93-87284-94-4.
  49. ^ "Mutable Variables and Reference Types". okmij.org . Получено 20 января 2024 г. .
  50. ^ Вермейр, Дирк (28 июня 2011 г.). Мультипарадигмальное программирование с использованием C++. Springer Science & Business Media. стр. 10–11. ISBN 978-1-4471-0311-0.
  51. ^ МакКоллин, Томас Гвинфрин; Морелл, Тобиас. «Игра парадигм: исследование удобства использования функциональных идиом в программировании игрового процесса» (PDF) . Университет Ольборга. стр. 6 . Получено 11 января 2022 г. .
  52. ^ ab Шаузер, Клаус Э.; Голдштейн, Сет К. (1995). "Сколько нестрогости требуют снисходительные программы?" (PDF) . Труды седьмой международной конференции по функциональным языкам программирования и компьютерной архитектуре - FPCA '95 . стр. 216–225. doi :10.1145/224164.224208. ISBN 0897917197. S2CID  2045943 . Получено 7 января 2022 г. .
  53. ^ Энналс, Роберт; Джонс, Саймон Пейтон (август 2003 г.). «Оптимистическая оценка: стратегия быстрой оценки для нестрогих программ».

Дальнейшее чтение

Внешние ссылки