Лямбда-исчислениеЛя́мбда-исчисле́ние (λ-исчисление) — формальная система, разработанная американским математиком Алонзо Чёрчем для формализации и анализа понятия вычислимости. Чистое λ-исчислениеЧистое λ-исчисление, термы которого, называемые также объектами («обами»), или λ-термами, построены исключительно из переменных применением аппликации и абстракции. Изначально наличие каких-либо констант не предполагается. Аппликация и абстракцияВ основу λ-исчисления положены две фундаментальные операции:
α-эквивалентностьОсновная форма эквивалентности, определяемая в лямбда-термах, это альфа-эквивалентность. Например, и — это альфа-эквивалентные лямбда-термы, которые оба представляют одну и ту же функцию — а именно, функцию тождества . Термы и не являются альфа-эквивалентными, так как являются свободными переменными. Вообще говоря, -преобразование — это переименование связанных переменных, не меняющее «смысла» терма. Структурно, два λ-терма -эквивалентны если это один и тот же терм, либо если какие-либо их составляющие термы соответственно -эквивалентны. Для абстракций, терм -эквивалентен , если это в котором все свободные появления заменены на , при условии, что 1.) не входит свободно в , и 2.) не входит свободно ни в одну абстракцию внутри (если такие есть). Требование, чтобы не была свободной переменной в — существенно, так как иначе она окажется «захваченной» абстракцией после -преобразования, и из свободной переменной в превратится в связанную переменную в . Второе требование необходимо, чтобы предотвратить случаи, подобные тому, когда, например, является частью . Тогда необходимо произвести -преобразование такой абстракции, например, в данном случае, в . β-редукцияПрименение некой функции к некоему аргументу выражается в -исчислении как аппликация -терма, выражающего эту функцию, и -терма аргумента. Например, применение функции к числу 3 выражается аппликацией в которой на первом месте находится соответствующая абстракция. Поскольку эта функция ставит в соответствие каждому значение , для вычисления результата необходимо заменить каждое свободное появление переменной в терме на терм 3. В результате получается . Это соображение в общем виде записывается как и носит название β-редукция. Выражение вида , то есть применение абстракции к некоему терму, называется редексом (redex). Несмотря на то, что β-редукция по сути является единственной «существенной» аксиомой λ-исчисления, она приводит к весьма содержательной и сложной теории. Вместе с ней λ-исчисление обладает свойством полноты по Тьюрингу и, следовательно, представляет собой простейший язык программирования. η-преобразование-преобразование выражает ту идею, что две функции являются идентичными тогда и только тогда, когда, будучи применёнными к любому аргументу, дают одинаковые результаты. -преобразование переводит друг в друга формулы и , но только если не появляется свободно в . Иначе, свободная переменная в после преобразования стала бы связанной внешней абстракцией , и наоборот; и тогда применение этих двух выражений сводилось бы -редукцией к разным результатам. Перевод в называют -редукцией, а перевод в — -экспансией. Каррирование (карринг)Функция двух переменных и может быть рассмотрена как функция одной переменной , возвращающая функцию одной переменной , то есть как выражение . Такой приём работает точно так же для функций любой арности. Это показывает, что функции многих переменных могут быть выражены в λ-исчислении и являются «синтаксическим сахаром». Описанный процесс превращения функций многих переменных в функцию одной переменной называется карринг (также: каррирование), в честь американского математика Хаскелла Карри, хотя первым его предложил Моисей Шейнфинкель (1924). Соответственно, аппликация n-арных функций — это на самом деле аппликация вложенных унарных функций, одна за другой. Например, для бинарных функций: (λxy. ...x...y... ) a b = (λx.λy. ...x...y... ) a b = (λx.(λy. ...x...y... )) a b = ((λx.(λy. ...x...y... )) a) b = (λy. ...a...y... ) b = ...a...b... Семантика бестипового λ-исчисленияТот факт, что термы λ-исчисления действуют как функции, применяемые к термам λ-исчисления (то есть, возможно, к самим себе), приводит к сложностям построения адекватной семантики λ-исчисления. Чтобы придать λ-исчислению какой-либо смысл, необходимо получить множество , в которое вкладывалось бы его пространство функций . В общем случае такого не существует по соображениям ограничений на мощности этих двух множеств, и функций из в : второе имеет бо́льшую мощность, чем первое. Эту трудность в начале 1970-х годов преодолел Дана Скотт, построив понятие области (изначально на полных решётках[1], в дальнейшем обобщив до полного частично упорядоченного множества со специальной топологией) и урезав до непрерывных в этой топологии функций[2]. На основе этих построений была создана денотационная семантика[англ.] языков программирования, в частности, благодаря тому, что с помощью них можно придать точный смысл таким двум важным конструкциям языков программирования, как рекурсия и типы данных. Связь с рекурсивными функциямиРекурсия — это определение функции через саму себя; на первый взгляд, лямбда-исчисление не позволяет этого, но это впечатление обманчиво. Например, рассмотрим рекурсивную функцию , вычисляющую факториал:
Эта функция не может быть выражена λ-термом (λn.{ 1, if n = 0; else n × (f (n-1)) }), так как в нём f является свободной переменной. Функция ссылается на саму себя посредством ссылки на своё имя, но в лямбда-исчислении у λ-термов имен нет. Тем не менее, λ-термы могут передаваться в качестве аргументов, включая передачу самим себе. В частности, терм-функция может получить в качестве аргумента саму себя, что приведёт к её связыванию с собственным параметром. Обычно этот параметр располагается первым в выражении. Когда λ-терм применяется к самому себе, его параметр связывается с тем же термом, формируя новый λ-терм, который выражает рекурсивную функцию. Однако, чтобы такая рекурсия была возможна, параметр, ссылающийся на себя (обозначим его как ), должен быть передан явно в качестве аргумента. Это обеспечивает корректный рекурсивный вызов:
где — это комбинатор самоприменения (самоаппликации), . Этот приём позволяет создавать рекурсивные функции, явно передавая λ-термы самим себе в качестве дополнительного аргумента. Но возможно выразить рекурсию и в общем виде, без самоаппликации, как видно после всего лишь двух преобразований абстракции: U (λh. λn. {1, if n = 0; else n × ((h h) (n-1))} ) = U (λh. (λr. λn. {1, if n = 0; else n × (r (n-1))}) (h h)) = (λg. U (λh. g (h h))) (λr. λn. {1, if n = 0; else n × (r (n-1))} ) Последнее выражение полностью эквивалентно исходному. Оно состоит из аппликации двух независимых λ-термов, где второй терм в выражении (ниже, ) — это просто лямбда-выражение рекурсивной функции без изменений, но с абстрагированным рекурсивным вызовом . А первый λ-терм в выражении — это некий комбинатор, называемый : G := (λr. λn. {1, if n = 0; else n × (r (n-1))}) Y := λg. U (λh. g (h h)) = λg. (λh. g (h h)) (λh. g (h h)) Этот комбинатор создает рекурсивную функцию из аргумента, являющегося закрытым (то есть в котором нет свободных переменных) λ-термом исходного выражения функции (то есть без удвоения параметра). Таким образом, Y g = (λh. g (h h)) (λh. g (h h)) = g ((λh. g (h h)) (λh. g (h h))) = g (Y g) то есть — это комбинатор неподвижной точки: он вычисляет неподвижную точку своего аргумента. Для закрытого λ-терма с соответствующей арностью, его неподвижная точка выражает рекурсивную функцию, так как , то есть аргумент который здесь создаётся для вызова внутри — это та же самая функция : F = Y (λr. λn. {1, if n = 0; else n × (r (n-1))}) = Y G = G (Y G) = (λr. λn. {1, if n = 0; else n × (r (n-1))}) (Y G) = ( λn. {1, if n = 0; else n × ((Y G) (n-1))}) = ( λn. {1, if n = 0; else n × (F (n-1))}) = G F Итак, — это закрытый функционал, то есть λ-терм, вызывающий свой аргумент в качестве функции; его неподвижная (зафиксированная, неизменяемая) точка — это функция (здесь, ), которая передаётся ему в качестве аргумента; а вызов той же самой функции и есть рекурсивный вызов. Комбинатором называют замкнутое лямбда-выражение, не содержащее свободных переменных и ссылающееся исключительно на свои аргументы. При этом оно может использовать комбинаторы из ограниченного набора базовых, считающихся примитивными. Существуют различные базисы комбинаторной логики, среди которых наиболее известны и . Эти базисы являются взаимно выразимыми, то есть любой комбинатор из одного базиса можно представить через комбинаторы другого. В частности, комбинаторы и , как и любые другие, могут быть выражены в обеих системах. Существует несколько (и вообще-то, бесконечно много) определений комбинаторов неподвижной точки. Вышеуказанное — одно из самых простых:
Используя стандартные комбинаторы и , Y g = U (λh. g (U h)) = U (λh. B g U h) = U (B g U) = U (CBU g) = BU(CBU) g = SSI(S(S(KS)K)(K(SII))) g В самом деле: U (B g U) = B g U (B g U) = g (U (B g U)) = g (Y g) Примерaми других комбинаторов неподвижной точки являются, например, комбинатор Тьюринга и комбинатор :
Итак, чтобы определить факториал как рекурсивную функцию, мы можем просто написать , где — число, для которого вычисляется факториал. Пусть , получаем (подразумевая каррирование, (a b c) = ((a b) c)): Y G 4 Y (λrn.{1, if n = 0; else n×(r (n-1))}) 4 (λrn.{1, if n = 0; else n×(r (n-1))}) (Y G) 4 (λn.{1, if n = 0; else n×(Y G (n-1))}) 4 {1, if 4 = 0; else 4×(Y G (4-1))} 4×(Y G 3) 4×(G (Y G) 3) 4×((λrn.{1, if n = 0; else n×(r (n-1))}) (Y G) 3) 4×{1, if 3 = 0; else 3×(Y G (3-1))} 4×(3×(G (Y G) 2)) 4×(3×{1, if 2 = 0; else 2×(Y G (2-1))}) 4×(3×(2×(G (Y G) 1))) 4×(3×(2×{1, if 1 = 0; else 1×(Y G (1-1))})) 4×(3×(2×(1×(G (Y G) 0)))) 4×(3×(2×(1×{1, if 0 = 0; else 0×(Y G (0-1))}))) 4×(3×(2×(1× 1 ))) 24 Итак, каждое определение рекурсивной функции может быть представлено как неподвижная точка соответствующего закрытого функционала, описывающего «один вычислительный шаг» рекурсивной функции. Следовательно, используя , любое рекурсивное определение может быть выражено как лямбда-выражение (λ-терм). В частности, мы можем определить вычитание, умножение, сравнение натуральных чисел и другие арифметические операции, используя рекурсию по необходимости, и выразить эти операции как λ-термы. В языках программированияВ языках программирования под «λ-исчислением» зачастую[источник не указан 98 дней] понимается механизм «анонимных функций» — callback-функций, которые можно определить прямо в том месте, где они используются, и которые имеют доступ ко всем переменным, видимым в месте их вызова в текущей функции (замыкание). См. также
Примечания
Литература
|
Portal di Ensiklopedia Dunia