Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Part5.doc
Скачиваний:
0
Добавлен:
15.12.2019
Размер:
202.75 Кб
Скачать

5 Лямбда-вирахування і типізація

5.1 Лямбда-вирахування

Основним поняттям у лямбда-вирахуванні є поняття виразу, або формули. Його можна визначити рекурсивно. Насамперед зафіксуємо набір ідентифікаторів, що надалі будемо називати змінними. Ми будемо використовувати як імена латинські букви x, y, f та інші. У формулах змінні звичайно позначають аргументи функцій, що задаються лямбда-виразами, однак сама по собі змінна є найпростішим видом виразу. Два інших види виразів – це визначення безіменної функції, тобто лямбда-виразу (абстракція) і застосування функції (аплікація).

Лямбда-вираз має вигляд (λx.e), де x – ім'я змінної, а e – вираз. Семантично такий вираз позначає функцію з аргументом x і тілом e. Застосування функції записується у вигляді (e1 e2), де e1 і e2 – вираз (e1 – функція, а e2 – її аргумент). Наведемо кілька прикладів.

1 λx.x – найпростіша функція, що видає свій аргумент; дужки опущені, оскільки це не викликає неоднозначності.

2 λf.λx.f x – функція з двома аргументами, що застосовує свій перший аргумент до другого. По суті, треба було б розставити дужки, щоб вираз набув вигляду λf.(λx.(f x)), однак прийнята угода, за якою операція застосування функції до аргументу має більш високий пріоритет, ніж операція утворення лямбда-виразу. При цьому функції застосовуються у порядку зліва направо, тобто вираз f x y розуміється як застосування функції f до аргументу x і застосування отриманого результату до аргументу y.

3 (λx.x x)(λx.x x) – застосування функції, заданої лямбда-виразом (λx.x x), до аргументу, що являє собою такий самий лямбда-вираз. Усередині тіла, що задає лямбда-вираз, аргумент x застосовується до себе.

Ми будемо розглядати не класичне лямбда-вирахування, у якому крім функцій та їх застосувань до інших функцій нічого немає, а деяке його розширення. У нашому розширеному лямбда-вирахуванні, крім безіменних функцій, заданих лямбда-виразами, будуть використовуватися константи, зміст яких заданий поза лямбда-вирахуванням. Це будуть звичні нам за мовами програмування константи, що позначають цілі числа, символи і логічні значення, константа, що позначає порожній список NIL, а також константи, що задають позначення примітивних функцій. Примітивні функції нашої системи варто розглянути окремо.

Багато примітивних функцій уже добре нам знайомі за іншими мовами програмування. Це звичайні арифметичні операції додавання, множення й інші, операції порівняння величин “більше”, “менше”, “дорівнює” та інші, операції над логічними значеннями “і”, “або” та інші. Єдина відмінність від стандартного використання цих та інших операцій в інших мовах програмування буде полягати у тому, що ми завжди будемо використовувати тільки префіксний запис операцій, тобто замість звичного 3+5 будемо записувати вираз + 3 5. Вираз + 3 2 1 4 також має сенс і являє собою застосування функції + до констант 3, 2, 1, 4, у результаті якого буде одержано 10.

У лямбда-вирахуванні визначені еквівалентні перетворення виразів. З їх допомогою можна переходити від одних виразів до інших, еквівалентних їм. У функціональному програмуванні аналогом до цього процесу є процес обчислення виразу, тому ми іноді будемо говорити про еквівалентне перетворення виразів у лямбда-вирахуванні як про обчислення виразів.

Перш ніж розглядати перетворення виразів, уведемо важливі поняття вільної і зв’язаної змінних. Неформально говорячи, входження змінної в деякий вираз буде зв’язаним, якщо воно знаходиться усередині деякого лямбда-виразу, у заголовку якого ця змінна згадана як аргумент. Іншими словами, якщо є вираз, у який як підвираз входить λx.e, то усі входження змінної x у вираз e будуть зв’язаними. Якщо змінна не є зв’язаною в деякому виразі, то вона у ньому буде вільною. Можна визначити поняття вільних і зв’язаних змінних і більш формально. Для цього розглянемо поняття “множини вільних змінних” деякого виразу. Ті змінні, які не будуть входити до цієї множини, будуть у цьому виразі зв’язаними.

Отже, якщо вираз E являє собою змінну x, то множина вільних змінних цього виразу F(E) містить тільки цю змінну: F(E) = { x }. Якщо вираз являє собою одну зі стандартних констант нашого розширеного лямбда-вирахування, то він не містить ні вільних, ні зв’язаних входжень змінних, F(c) = {}. Якщо вираз є застосуванням функції до аргументу і має вигляд E = e1 e2, то множина вільних змінних цього виразу є об’єднанням множин вільних змінних виразів e1 і e2: F(E) = F(e1  F(e2). Нарешті, якщо вираз має вигляд лямбда-виразу E = λx.e, то його множина вільних змінних вийде, якщо з множини вільних змінних виразу e видалити змінну x: F(E) = F(e) \ x }.

Слід зазначити, що та сама змінна може бути зв’язаною в деякому виразі й одночасно вільною в деякому його підвиразі. Так, наприклад, у виразі λf.f x змінна x – вільна, а змінна f – зв’язана. Однак якщо розглядати тільки тіло цього лямбда-виразу f x, то в ньому обидві змінні f і x вільні. Взагалі, вирази, що містять вільні змінні, хоча і є формально припустимими, але, як правило, невизначені. Наприклад, вираз λf.f x являє собою функцію, що застосовує свій аргумент до змінної x. Оскільки змінна x – вільна, то її зміст у цьому виразі не визначений. Якщо ж зв’язати цю змінну, то ми одержимо вже цілком осмислений вираз λx.λf.f x, який являє собою функцію, що застосовує свій другий аргумент до першого. Звичайно ми розглядаємо вирази, що містять вільні змінні, тільки як складові частини інших виразів, у яких розглянуті змінні вже є зв’язаними.

Ми розглянемо чотири способи перетворення виразів у лямбда-вирахуванні. Перше з розглянутих перетворень називається перейменуванням змінних або α-перетворенням. Зміст цього перетворення полягає у тому, що суть функції не змінюється, якщо замінити ім’я її формального аргументу. Формально α-перетворення полягає в заміні у виразі λx.e імені змінної x на будь-яке інше ім’я з одночасною заміною усіх вільних входжень цієї змінної у вираз e. Перетворення можливе, якщо тільки нова змінна вже не входить вільно у вираз e, а також якщо при цьому вільне входження змінної не виявиться зв’язаним. Так, наприклад, у виразі λx.λf.f x y можна замінити змінну x, скажемо, на змінну z. У результаті отримаємо вираз λz.λf.f z y. Зрозуміло, новий вираз еквівалентний вихідному і має той самий зміст. Однак у тому самому виразі змінну x не можна замінити на y, оскільки змінна y входить у тіло лямбда-виразу вільно, так що вираз, який було одержано після заміни λy.λf.f y y, уже буде мати інший зміст – у ньому обидва входження змінної y виявляються зв’язаними. Не можна також зробити і заміну x на f, оскільки це приведе до того, що в тілі лямбда-виразу вільне входження змінної x перетвориться у зв’язане входження змінної f, і вираз λf.λf.f  f  y також буде мати вже інший зміст.

Перейменування не приводить до зміни довжини або структури виразу, воно застосовується не як засіб для обчислення виразу, а тільки для технічних цілей. Два наступні перетворення – це обчислювальні перетворення, що приводять до спрощення виразу, тобто зведення його до найкоротшої форми. Тому ці перетворення називають ще редукціями від слова reduce – скорочувати. У програмуванні ці дві редукції відповідають застосуванню “убудованих”, або “визначених програмістом”, функцій до фактичного аргументу або аргументів.

Перетворення, яке називають δ-редукцією, відповідає застосуванню убудованої функції до константних аргументів. Правило δ-редукції має такий вигляд. Нехай є вираз e  e1 e2 ... ek, де e – константа, що представляє ім’я убудованої функції з k аргументами, а e1, e2, ..., ek – значення, що можуть служити аргументами цієї функції. Тоді такий вираз можна замінити на еквівалентний йому вираз, представлений значенням, що випливає як результат застосування функції до заданих значень аргументу. Так, наприклад, якщо константа + представляє функцію арифметичного додавання цілих, а константа OR – функцію логічного “або”, то в результаті δ-редукції вираз + 1 4 може бути перетворений у вираз 5, а вираз OR TRUE FALSE – у вираз TRUE.

Перетворення, що називається β-редукцією, відповідає застосуванню функції, представленої лямбда-виразом, до аргументу. Якщо вихідний вираз мав вигляд (λx.e)  a, то в результаті застосування β-редукції він буде перетворений у e{x|a} – вираз e, у якому усі вільні входження змінної x замінені на вираз a. Наприклад, вираз ((λx.+  x  x) 3) у результаті застосування β-редукції буде перетворений у (+ 3 3), який, у свою чергу, може бути перетворений у ( 6 ) за допомогою застосування δ-редукції.

Якщо в деякий вираз входить як підвираз такий вираз, до якого можна застосувати одну з редукцій, то такий підвираз називається редексом (від redexreducible expression). Таким чином, процес перетворення виразу зводиться в основному до застосування β- і δ-редукцій до редексів, що містяться у вихідному виразі.

Відмітимо, що при застосуванні β-редукції заміні підлягають саме вільні змінні тіла лямбда-виразу. Розглянемо такий вираз: ((λx.+ x ((λx.* x x) 2)) 4). У цьому виразі є два підвирази-редекси, до яких можна застосувати β-редукцію. Перший з цих редексів – це сам вираз цілком, другий – підкреслений підвираз. Якщо застосувати β-редукцію до першого з цих редексів – зовнішнього, то отримаємо вираз (+ 4 ((λx.* x x) 2)). Тут при застосуванні редукції вільне входження змінної x було замінене константою 4, а зв’язане входження цієї змінної у внутрішньому редексі залишилося незмінним. Ми можемо, однак, застосувати β-редукцію до внутрішнього редексу, тоді вихідний вираз буде перетворено у вираз ((λx. +  x (*  2  2))  4).

Останнє з розглянутих нами чотирьох перетворень, що називається η-перетворенням, виражає той факт, що дві функції, які при застосуванні до того самого аргументу дають той самий результат, еквівалентні. Формально η-перетворення може бути записане в такий спосіб: λx.E  x еквівалентно E. Дійсно, перший з цих виразів – λx.E  x – являє собою функцію, що при заданні деякого значення аргументу видає результат, еквівалентний результатові застосування функції E до цього аргументу. Можливість застосування η-перетворення означає, що отримана функція еквівалентна функції E.

Основне призначення лямбда-вирахування полягає у тому, щоб показати, що будь-яка обчислювана функція може бути представлена у вигляді лямбда-виразу. При цьому редукції й інші перетворення служать необхідним апаратом для доведення того, що побудована функція дійсно дає потрібний результат при застосуванні її до тих або інших аргументів. Наприклад, легко бачити, що функція, представлена лямбда-виразом (λx.* x x), являє собою функцію піднесення до квадрата. Щоб показати, що це дійсно так, можна спробувати застосувати цю функцію до деяких аргументів і подивитися, чи дійсно одержимо потрібний результат. Перевіримо, наприклад, що функція дійсно видасть результат 36, якщо застосувати її до аргументу 6. Для цього складемо вираз ( (λx. *  x  x) 6) і проведемо його редукції. Спочатку в результаті застосування β-редукції отримаємо вираз (*  6 6), до якого можна, у свою чергу, застосувати δ-редукцію й остаточно одержати в результаті значення 36.

Узагалі, редукції можна застосовувати доти, поки у виразі міститься хоча б один редекс. Якщо жодного редекса у виразі немає, то говорять, що вираз знаходиться в нормальній формі. Наш процес ”налагодження” зводиться до перетворення вихідного виразу до його нормальної форми.

Нормальна форма існує не завжди, так само, як не будь-яка програма зобов’язана завершитися видачею результату. Наприклад, як розглядалося нами раніше, вираз (λx.x x)(λx.x x) не має нормальної форми. Дійсно, саме цей вираз не є нормальною формою, оскільки до нього можна застосувати β-редукцію. Однак при підставлені аргументу (λx.x  x) замість x у тіло лямбда-виразу “x  x” ми одержимо знову той самий вираз (λx.x x)(λx.x x). Вираз, подібний тільки що розглянутому, є найпростішою формою програми, що зациклюється.

Теорема, відома як теорема Черча-Россера, стверджує, що, якщо ми почнемо з терма T і проведемо дві скінченні послідовності редукцій, завжди будуть існувати ще дві послідовності редукцій, які приведуть нас до одного і того самого терма (хоча він може і не знаходитися в нормальній формі).

Іншими словами, якщо T S1 і T S2 , то існує терм U , такий, що S1 U і S2 U .

Ця теорема має кілька важливих наслідків. Зокрема, нормальна форма, якщо вона існує, єдина з точністю до α-редукції.

Ми розглянули деяке розширення лямбда-вирахування. Існує також чисте лямбда-вирахування, де розширення не додаються ззовні, а будуються за допомогою комбінаторів.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]