Serguey Zefirov ([info]thesz) wrote,

Лямбда-исчисление.

Коротко и неформально.

Лямбда-исчисление - это правила построения и вычисления безымянных функций.

И перейдем сразу к примерам.

Вот запись функции, которая получает на вход число и вычисляет значение, большее его на единицу:

    (\x.x+1)
Что здесь к чему? Сама функция записана в скобочках. Символ '\' будет впредь обозначать строчную греческую букву λ (кстати, в Haskell использется именно это обозначение, и такая безымянная функция в Haskell будет выглядеть \x -> x+1). Далее, до точки, идут аргументы функции, а после точки -- выражение.

Применение функций к аргументам записывается просто -- через пробел. Вот так:

    (\x.x+1) 2
Мы можем упрощать записанное нами выражение, подставляя вместо переменных их значения (бета-упрощение, бета-редукция):
    (\x.x+1) 2 -- применяем бета-редукцию, подстановку значений аргументов.
==> (2+1) -- применяем дельта-редукцию, то есть, вычисление функции от аргументов.
==> 3
Так оно и происходит. И даже в реальной жизни, когда идет рассуждение о программе.

Еще немного поупражняемся. Сделаем-ка функцию от двух аргументов, все ту же сумму:

    (\x y.x+y)
Ага, сделали. Что будет, если мы применим ее к одному аргументу? Сейчас выясним:
    (\x y.x+y) 1 -- примением бета-редукцию, заменим x на 1.
==> (\y.1+y)
Получили все ту же функцию увеличения на единицу, вид, так сказать, в профиль. То есть, если мы применим функцию к неполному числу аргументов, то получим другую функцию, которая должна получить оставшиеся аргументы.

И, в лучших традициях формальной математики, перепишем только что обнаруженный нами факт:

    (\x y.x+y) эквивалентно (\x.(\y.x+y))
Этот прием назвается карринг (currying), и именно он не стал шонфикелингом. ;)

Я еще упомяну о необходимости переименовывать переменные, чтобы после подстановки значений не возникало проблем, какой это y -- этот y является нашей переменной, или результатом подстановки?

Вот. А пока займемся другой проблемой. Посмотрим на выражение (\x.x x)(\x.x x). Попробуем его упростить:

    (\x.x x) (\x.x x) -- переименуем x в самом левом выражении в z.
    (\z.z z) (\x.x x) -- затем подставим вместо z его значение (\x.x x).
    ((\x.x x) (\x.x x)) -- где-то мы это уже видели.
Мы получили невычислимое выражение. Оно настолько просто, что его можно вычислять до бесконечности. ;) И оно чуть-чуть интереснее, чем банальное деление на ноль.

Зато это дает возможность продемонстрировать еще кое-что. На этот раз мы возьмем в качестве кандидата на упрощение следующее выражение -- (\x.1)((\x.x x) (\x.x x)).

Если мы начнем вычислять аргумент, то никогда не остановимся. Однако, если мы начнем сразу с тела функции ((\x.1) -- функция, которая всегда вычисляется в 1), то сразу придем к ответу -- 1. Отличие разительно, не находите?

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

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

В свою очередь, первый вариант применяется энергичными (жадными) языками и легче для реализации. OCaml и LISP используют именно его.

Вообще, никакая современная программа не может обойтись без ленивых вычислений. Тот же if..then..else.. откладывает вычисления.

Кстати, а давайте сочиним условную конструкцию с помощью лямбда-исчисления? Хорошо, тем более, что это очень просто.

Что делает if c then a else b? В зависимости от значения c (condition, условия) оно выбирает либо значение a, либо значение b. Итак, конструкия if c then a else b -- это функция с тремя параметрами:

    (\c.a.b.???)
Нам надо выяснить, что может находиться вместо знаков вопроса.

Во-первых, там не может быть никаких if c then a else b, поскольку мы именно это и определяем. Во-вторых, там должен находится выбор значений a или b в зависимости от c, при этом на структуру самого c ограничений нет. Если мы примем значение (\a.b.a) за логическую истину, а значение (\a.b.b) -- за ложь, то можно подставить вместо знаков вопроса выражение c a b. Сейчас проверим:

    (\c.a.b.c a b) (\a.b.a) 1 2 -- то же, что if true then 1 else 2.
==> (\a.b.(\x.y.x) a b) 1 2
==> (\a.b.a) 1 2
==> 1
Вроде, правильно. Как записать if false then 1 else 2, и во что это раскроется, я оставлю в качестве упражнения.

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

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

Ну, и в качестве заключения.

Я довольно долго исследовал всякие системы на предмет упрощения труда программиста. На данный момент я считаю, что лямбда-исчисление с ленивым порядком вычислений позволяет записать решения самым коротким способом при самой лучшей эффективности исполнения. То есть, если надо записать решение задачи короче, то будет значительно менее эффективно, при разнице в длине записи порядка десятка процентов, и не всегда в пользу не-Haskell записи (см. сравнение Haskell vs. Maude, есть такое в сети). А если надо эффективнее, то получится значительно длинее (Haskell vs. C, такое тоже есть).

На этом, пожалуй, все. ;)

Tags: лямбда исчисление

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    Your reply will be screened

    Your IP address will be recorded 

  • 85 comments

[info]morvic

June 15 2005, 17:34:05 UTC 6 years ago

здорово!
пиши исчо!
Предлагаю следующие темы
1. Деревья решений. Прямая и обратная цепочка рассуждений. Алгоритм CLS
2. Логика предикатов. Основные понятия. Особенности использования кванторов.
3. Индуктивный вывод. Виды математической индукции.
4. Рекурсия. Виды рекурсий.
5. Метод доказательства от противного.
6. Приведение к префиксной нормальной форме. Основные правила.
7. Скалемизация.
8. Приведение к клаузальной нормальной форме.
9. Метод резолюции для высказываний.
10. Алгоритм унификации. НОУ. Правила допустимости подстановок.
11. Метод резолюции для предикатов. Особенности метода при использовании равенств.
12. Генетические алгоритмы. Основные характеристики. Примеры. Разновидности алгоритмов
13. Нейронные сети. Основные понятия. Вероятностные сети. Карта Кохонена. LVQ. Сети Хоппфилда и BAM. Сеть BP – SOM

[info]thesz

June 15 2005, 17:39:43 UTC 6 years ago

Обязательно.

Как только будет время.

Начну прямо с пятого пункта. ;)

А каким ветром тебя сюда занесло? ;)

[info]askhome

August 29 2005, 09:48:44 UTC 6 years ago

Книги на русском языке по ламбда-исчислению и комбинат

По-моему, российское сообщество аппликативных вычислений разделилось по написанию: лЯмбда-исчисление и лАмбда-исчисление. Это - в порядке юмора. Но поисковые механизмы Web это различают. Имеется целый ряд книг, за последнее время изданных в России. Подробное и элементарное изложение, есть глоссарии и библиографии. Вот некоторые ссылки: http://jurinfor.exponenta.ru ; http://vew.0catch.com ; http://www.microsoft.com/Rus/Msdnaa/Curricula/Default.mspx. Многие из книг с этих ссылок можно бесплатно загрузить. Перспективы исследований и открытые проблемы можно узнать на ссылке http://kuzichev.boom.ru.

[info]thesz

August 29 2005, 09:54:56 UTC 6 years ago

Re: Книги на русском языке по ламбда-исчислению и комбин

Ага.

Книга Вольфенгаген про вычисления с объектами у меня есть, но я до нее все никак не доберусь.

Ссылка на майкрософт интересна. Я сейчас эти две ссылки отдельным постом вынесу.

Anonymous

4 years ago

[info]thesz

4 years ago

[info]thesz

6 years ago

[info]yellow_spots

December 12 2006, 02:41:51 UTC 5 years ago

отличная вещь для тех, кто хочет успеть выучить хотя бы что-нибудь в ночь перед экзамом.
спасибо.

[info]thesz

December 12 2006, 05:05:46 UTC 5 years ago

А это идея! ;)

[info]thesz

5 years ago

[info]thesz

5 years ago

[info]thesz

5 years ago

[info]lapanoid

3 years ago

[info]thesz

3 years ago

[info]lapanoid

3 years ago

[info]thesz

3 years ago

[info]lynoxod

December 12 2006, 05:25:22 UTC 5 years ago

Елки, давно все думаю - а может таки "выучить" Хаскель?

[info]thesz

December 12 2006, 07:44:46 UTC 5 years ago

Зачем его учить? Его использовать надо. ;)

[info]lynoxod

5 years ago

[info]thesz

5 years ago

[info]nealar

5 years ago

[info]alll

4 years ago

[info]bilet_v_zirk

December 12 2006, 10:15:53 UTC 5 years ago

ы

[info]thesz

December 12 2006, 10:22:37 UTC 5 years ago

Ну, извини. Доказательства я опустил, поскольку беру их на веру, многие не знаю, кое-какие не помню. Если хочешь, могу найти ссылки на литературу с доказательствами. ;)

[info]thesz

5 years ago

[info]nealar

December 12 2006, 11:09:50 UTC 5 years ago

Как вот так вот тречить комменты ко всем своим старым записям?

[info]thesz

December 12 2006, 11:11:51 UTC 5 years ago

У меня всюду стоит "присылать комменты на почту." Вот и приходят. ;)

[info]_winnie

December 12 2006, 12:14:55 UTC 5 years ago

>(\x y.x+y) эквивалентно (\x.(\y.x+y))
А как в функцию (\x.(\y.x+y)) "передать" y, если "извне" виден только один параметр - (\x.что-то-там)?

[info]_winnie

December 12 2006, 12:18:24 UTC 5 years ago

Понял. Передать надо в возвращаемое значение, которое тоже функция :]

[info]_winnie

5 years ago

[info]thesz

5 years ago

[info]ni_ten_ichi_ryu

June 23 2008, 14:14:35 UTC 3 years ago

прошу прощения, но не ответите ли вы на один простой (как мне кажется) вопрос по теме? не конкретно по вашему посту, но тематика та же (лямбда исчесление).

вопрос: что означает запись 2 переменных подряд? например, \x.xx.
будели ли верным след., хотя бы отчасти:
(\x.xx)(\y.y)=(\y.y)(\y.y)=\y.y

т.е., нечто вроде "генератора" аппликаций.

заранее спасибо.

PS: возможно, терминология немного отличается, я читаю "ламбда исчисление" Берендрегта.

[info]thesz

June 23 2008, 14:18:03 UTC 3 years ago

Будет верным. Практически полностью. ;)

Запись \x...\y...(x y) означает применение выражения x к аргументу y.

[info]thesz

3 years ago

[info]antidvacher

June 26 2008, 14:00:59 UTC 3 years ago

Это псевдокод?

[info]thesz

June 26 2008, 14:18:45 UTC 3 years ago

В смысле?

[info]thesz

3 years ago

[info]sokoltry

September 24 2008, 19:35:27 UTC 3 years ago

Привет. Я что-то никак не могу въехать в этот хитрый редуцирующийся сам в себя терм: (\х. х х)(\х. х х). Запись х у - применение выражения x к аргументу y. Но у нас х не функция, а число. Однако, как я понял, в лямбда исчислении просто число - это функция типа (\y. x), что ей не передавай вернет х. Т.е. функция (\х. х х) что ей передашь, то и вернет, также как и вся эта хитрая (\х. х х)(\х. х х)?

[info]thesz

September 30 2008, 11:23:24 UTC 3 years ago

Советую от чисел абстрагироваться. ;)

[info]thesz

2 years ago

[info]thesz

3 years ago

[info]thesz

3 years ago

[info]vieni

March 18 2009, 11:52:28 UTC 3 years ago

Здорово! Все понятно, кроме того, что такое шонфикелинг.

[info]thesz

March 18 2009, 21:18:15 UTC 3 years ago

То же самое, что и карринг. Он был изобретён Шонфинкелем и ещё кем-то из советских учёных.

[info]tarhitai

June 2 2009, 22:43:17 UTC 2 years ago

Я случайно попал на эту страницу
у меня вопрос можете подсказать что можно сделать вот с этим

> Непосредственно само задание:
>
> lambdaX.if(>X3)X(>X5)
>
> Что с этим нужно сделать:
> Произвести Beta-редукцию, КЛ,КЛ-редукцию, граф, редукцию графа, код де'Брюйна, КАМ-код и вычислить.

Если откликнитесть, буду очень признателен

[info]thesz

June 3 2009, 00:45:08 UTC 2 years ago

Нет, не помогу.

Anonymous

October 25 2009, 21:46:31 UTC 2 years ago

В статье есть существенный недостаток, который, наверняка, спецам не виден, но для начинающего просто "бросается" в глаза =)

Конкретно: синтаксис записи лямбда-функции таков:
\ (лямбда) x (аргумент) . (разделитель аргументов и самого тела функции) x+1 (собственно функция)
То есть '.' - это разделитель аргументов и тела функции, если я правильно понял.
Тогда что значит (\c.a.b.???) ???
Почему там аж три точки?

Надеюсь на благосклонный ответ =)

[info]thesz

October 25 2009, 22:52:09 UTC 2 years ago

Сокращение от (\a.\b.\c.f a b c).

Должно быть (\a b c. f a b c), конечно.

Anonymous

November 9 2009, 22:37:53 UTC 2 years ago

Вопрос по поводу.

Есть программка для вычисления лямбда-выражений (www.cburch.com/proj/lambda).
Так вот она (и не только она) говорят мне, что:
(\a.a a)(x y) => (beta-ред.) => x y (x y)
Я решительно не понимаю, почему именно так, а не
x y x y
или
(x y) (x y)
Не подскажите? Буду благодарен.

[info]thesz

November 9 2009, 22:43:05 UTC 2 years ago

Re: Вопрос по поводу.

f - функция от двух аргументов, допустим.
f a b - некоторое значение
(f a) b - карринг f.
Пусть теперь b = x y
(f a) (x y)

Раскрываем скобочки вокруг (f a), получим f a (x y).

Anonymous

April 17 2010, 21:13:37 UTC 2 years ago

Вау! Просто отлично!
Очень давно мучал этот вопрос.
Скажите, а есть ли специальная литература посвещеная вот таким вот трюкам в терминах лямбд?

[info]thesz

April 17 2010, 21:36:21 UTC 2 years ago

Да здесь трюков особых нет. Здесь описано что-то вроде http://en.wikipedia.org/wiki/Mogensen-Scott_encoding или близкого к нему - как закодировать конструкторы в терминах лямбд.

[info]nikita_timofeev

October 15 2010, 09:35:52 UTC 1 year ago

Пост пятилетней давности занимает вторую позицию (после Википедии) в ответе гугла по запросу «лямбда исчисление». Примечательно, что третью позицию занимает пост содержащий только ссылку сюда.
В университете лямбда исчисление я так и не понял. Сейчас, имея под рукой массу лямбда-решателей, во многом разобрался, но возникли новые вопросы связанные с типизацией лямбда-выражений. Например выражение (\x.x x) (\x.x x) не вычисляется в Haskell по причине того, что его не удаётся типизировать. Насколько я понял в первом приближении, типизация Чёрча лишает лямбда исчисление полноты по Тьюрингу. Это справедливо для сильной типизации в принципе или это ограничение как-то обходится?

[info]thesz

October 15 2010, 20:17:02 UTC 1 year ago

Про (\x -> x x). В процессе вывода получается решение вида a :: a -> a, что приводит к потенциальному зацикливанию и возможности внесения противоречий в систему типов.

http://fprog.ru/2010/issue5/roman-dushkin-hindley-milner/

С допущением правил вида a :: a -> a система типов становится противоречивой, начинает пропускать больше ошибок.

Тьюринг-полноты лямбда-исчисление лишает строгая типизация с простыми типами, без переменных типов. Если ввести переменные типов, то Тьюринг полнота возвращается и снова пропадает, если мы добираемся до зависимых типов (здесь опять система типов с Тьюринг-полными вычислениями становится противоречивой).

Это моё понимание проблемы на текущий момент. Возможно, я не совсем прав, так что продолжайте изучать сей интересный предмет. ;)

[info]aamonster

January 6 2011, 19:19:36 UTC 1 year ago

Ребе, сорри, что поднимаю древнюю тему - просто как раз сейчас нашёл время для вникания в лямбды и иже с ними (== вместо чтения книжек в метро написал реализацию разбора и редукции на це++).
И что-то я озадачен двумя вещами:
1. Нужны ли переименования переменных при редукции? (у Харрисона написано, что нужны, но что-то они у меня в коде даже не возникли: внутрь (λx.(терм)) контекст просто уходит уже без назначенного x значения...)
Да, реализация у меня тупая: терм - либо атом, либо λатом.терм, либо пара (терм терм). Редукция - тупая рекурсивная с передачей контекста:
TermPtr reduce(TermPtr term, Context context)
- атом редуцируется либо в своё значение из контекста (если есть), либо сам в себя.
- λ атом.терм - атом не трогаем, для терма вызываем reduce(терм,context-атом)
- (λ атом.терм) терм2 - заменяем на reduce(терм, context+"атом=терм2")
- прочие пары (терм1 терм2) - на (reduce(терм1,context) reduce(терм2,context))
2. Что-то я туплю: если прогонять мой рекурсивный код до упора - то на (λf.λx.f(f x)) (λf.λx.f(f x)) f x он лажает... если разрешить ему выполнять за раз только одну бета-редукцию - то всё ok, но требуется не один проход по выражению. Пока пробую понять сам, но не откажусь от совета (может, на сторонний взгляд там сразу понятно, в чём косяк). Код тут не кладу, дабы не позориться =), но готов показать.

[info]aamonster

January 6 2011, 23:00:37 UTC 1 year ago

Первый вопрос снимается, перечитал кусок Харрисона, нашёл пример, выявил косяк. Нотация де Брауна или как его там - наше всё =).

[info]thesz

1 year ago

[info]aamonster

1 year ago

[info]aamonster

1 year ago

[info]aamonster

1 year ago

[info]thesz

1 year ago

[info]aamonster

1 year ago

[info]aamonster

1 year ago

[info]thesz

1 year ago

[info]aamonster

1 year ago

[info]thesz

1 year ago

[info]Roman Kovalev

November 9 2011, 15:54:36 UTC 6 months ago

интересно, а

> Если мы примем значение (\a.b.a) за логическую истину, а значение (\a.b.b)

как это понять - функция от 3 аргументов, возвращающая ничего(nil/void и т.д.)?

>В свою очередь, первый вариант применяется энергичными (жадными) языками и легче для реализации. OCaml и LISP используют именно его.

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

[info]thesz

November 9 2011, 19:50:39 UTC 6 months ago

Re: интересно, а

>как это понять - функция от 3 аргументов, возвращающая ничего(nil/void и т.д.)?

Функция от двух аргументов, возвращающая один из них.

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

Я не знаю, как работает лямбда в Лиспе, поэтому ничего не могу сказать.

[info]thesz

6 months ago

[info]thesz

6 months ago

[info]alwgel

March 25 2012, 06:44:17 UTC 2 months ago

Уважаемый thesz, что-о я сразу застрял, помогите, пожалуйста!

«перейдем сразу к примерам.»… «(\x.x+1)»… «Сама функция записана в скобочках
до точки, идут аргументы функции, а после точки -- выражение.»…
ПОКА ВСЁ ПОНЯТНО! :О)
НО ВОТ ДАЛЕЕ:
«Посмотрим на выражение (\x.x x)(\x.x x)»
ЕСЛИ Я ПРАВИЛЬНО ПОНЯЛ, ЭТО ПРИМЕНЕНИЕ ФУНКЦИИ (\x.x x) К АРГУМЕНТУ (\x.x x). НО КАКИМ ВЫРАЖЕНИЕМ ИЛИ ВЫРАЖЕНИЕМ ЧЕГО ЯВЛЯЮТСЯ ДВЕ БУКОВКИ X ЧЕРЕЗ ПРОБЕЛ ПОСЛЕ ТОЧКИ? ОБЪЯСНИТЕ, ПОЖАЛУЙСТА, ТУПОМУ. ВЕРИТЕ, СПАТЬ НЕ МОГУ.
Create an Account
Forgot your login or password?
Facebook Twitter More login options
English • Español • Deutsch • Русский…