- •1.2. Алгоритм
- •1.3. Предмет науки програмування
- •1.4. Застосування мов програмування
- •1.5. Стандартизація мов програмування
- •1.6. Середовище розробки
- •1.7. Тестування і верифікація програм
- •Лекція 2. Алгоритми і обчислення
- •2.1. Алгоритми
- •2.2. Обчислювальні машини
- •2.3. Основи фон-нейманівської архітектури
- •Лекція 3. Парадигми програмування
- •3.1. Базові парадигми програмування
- •Процедурне програмування
- •Абстрактна семантика процедурних програм.
- •Денотаційна семантика.
- •Операційна семантика
- •Структурність і модульність
- •3. 3. Модульне програмування
- •3.4. Об’єктно-орієнтовне програмування
- •3.5. Функціональне програмування
- •3.6. Логічне програмування
- •3.6. Алгебраїчна парадигма
- •Висновок
- •Лекція 4. Огляд основних мов програмування
- •4.1. Мова Picmkalkul Конрада Цузе
- •4.1.1. Історичні передумови
- •4.1.2. Огляд мови
- •4.2. Мінімальне програмування на апаратному рівні: псевдокоди
- •4.2.1. Мова Short Code
- •4.2.2. Система Speedcoding
- •4.2.3. Система "компіляції" univac
- •4.2.4. Суміжна робота
- •4.3. Комп'ютер ibm 704 і мова fortran
- •4.3.1. Історична ретроспектива
- •4.3.2. Процес розробки
- •4.3.3. Огляд мови fortran I
- •4.3.4. Огляд мови fortran II
- •4.3.5. Мови fortran IV, fortran 77 і fortran 90
- •4.4. Функціональне програмування: мова lisp
- •4.4.1. Витоки робіт в області штучного інтелекту та обробка списків
- •4.4.2. Процес розробки мови lisp
- •4.4.3. Огляд мови
- •4.4.5. Два нащадки мови lisp
- •4.4.6. Споріднені мови
- •4.5. Перший крок до удосконалення: мова algol 60
- •4.5.1. Історична ретроспектива
- •4.5.2. Початкова стадія розробки
- •4.5.3. Огляд мови algol 58
- •4.5.4. Прийняття звіту про мову algol 58
- •4.5.5. Процес розробки мови algol 60
- •4.5.6. Огляд мови algol 60
- •4.5.7. Оцінка мови algol 60
- •Лекція 5. Трансляція і формальні граматики
- •5.1. Транслятори
- •5.2. Процес трансляції
- •5.3. Формальні граматики. Нбф-граматика
- •Лекція 6. Java - мова ооп
- •6.2. Основні властивості програм і перші приклади
- •6.3. Арифметичні і побітові оператори мови Java
3.5. Функціональне програмування
Функціональний стиль програмування оснований на використанні тільки процедур-функцій. Роль змінних виконують параметри функцій. Присвоєння значень проходить тільки при визначенні (заданні) аргументів при звертанні до функцій. Послідовна композиція операторів замінюється покроковим обчисленням аргументів при виклику функції, умовна композиція операторів – такою ж композицією виразів, циклічна композиція – рекурсією.
Подумайте про архітектурні концепції розглянутих нами типів обчислювачів та їх можливості по реалізації функціонального стилю!!!!!
Може здатися, що арсенал таких засобів програмування досить бідний. Але це далеко не так!
Програми, написані функціональною мовою програмування (ФМП), традиційно є коротшими, нагляднішими і простішими для розуміння. До того ж вони є і надійнішими – «змінна» (новий екземпляр параметра функції) створюється тільки в момент присвоєння їй значення і не змінює його за весь час свого існування. Використання рекурсії при виклику функції добре співвідноситься з рекурсивним стилем опису типів значень і таких конструкцій, як виразів, зокрема – звернення до функції (вираз будується за допомогою композиції звертань до функцій-операцій).
В якості прикладу ФМП приведемо лісповське визначення рекурсивної функції append, яка вставляє в початок списку У (її другого параметру) всі елементи списку Х, зберігаючи їх порядок:
(define
append (lambda (X Y)
(cond (( NULL X) Y)
(T (cons (car X) (append (cdr X) Y))
)
)
)
)
Узагальнимо приведені рішення для функціональної парадигми
Дані тут представляються як функціональні вирази, а алгоритми - як системи функціональних рівнянь. Кожній програмі відповідає часткова функція чи множина функцій, що складають найменший розв’язок канонічної системи функціональних рівнянь, тобто системи вигляду
(x)
=
(f,x),i=
1,2 ,...,m,
де х =
(
),f
= (
).
Існує велика кількість функціональних мов програмування, починаючи з мови LISP та її нащадків і до сучасних мов, таких як SHEMA і ML. Для функціональних програм існує значно більше стратегій обчислень, ніж для процедурних. Добре відомі відмінності між такими стратегіями як “виклик за іменем” і “виклик за значенням”, паралельна і послідовна підстановки і іншими, що використовуються при виконанні функціональних програм. Але у всіх випадках елементарним кроком обчислень є підстановка функціонального означення і спрощення виразів. До того ж, як тільки обчислення закінчуються, то результат визначений однозначно. Іншими словами, стратегія лише визначає час, необхідний на обчислення, і як ці обчислення проводяться, але не остаточний результат.
Абстрактна семантика функціональних програм.
Якщо
= (f,x),
де х = ( ),f = ( ),i = 1,2,..., m – функціональна програма, то (f,x), називається функціональним виразом, тобто є виразом, побудованим із констант, змінних і символів функцій, що визначаються, і символів раніше побудованих функцій. Отже, на функціональну програму можна дивитися як на функціональне рівняння.
Денотаційна
семантика. Нехай D,
як і раніше, є область даних для
змінних програм. Тоді
: Dn
D.
Слід зазначити, що коли у функціональному
рівнянні розглядати лише повністю
визначені функції, то розв’язку може
й не існувати. Якщо ж припустити, що
функції
часткові,
то розв’язок існує завжди, але цей
розв’язок може бути не єдиний.
Приклад 3.1. Нехай програма Р маг вигляд
F(x, у) = if х = у then у + 1 else F(х, F(x -1, у + 1)).
Функції
(x, у) = if
х = у then
у + 1 else
x + 1;
(х,у) = if
х ≥ 1 then
х + 1 else
у - 1;
(x, у) = if x ≥ у &
((x - y)mod
2 = 0) then у + 1
else ┴,
де ┴ - символ невизначеності, є розв’язком програми-рівняння Р. Це означає, що коли всі входження функції F в програмі замінити на функцію fі , i = 1,2,3, то зліва і справа від знака рівності будуть стояти однакові функції, тобто
(x, у) = if
х = у then
у + 1 else
(x,
(x
- 1,у + 1)).
Дійсно, розглянемо, наприклад, функцію . Маємо
(x, у) = if
х = у then
у + 1 else
(x,
(x -
1 ,у+ 1));
(х, (x - 1,y + 1)) = if х = (х - 1,y + 1) then (x - 1,у + 1) + 1 else х+ 1; (x - 1, у + 1) = if х - 1 = у + 1 then у + 2 else х.
Розглянемо два випадки:
х-1 = у+1 (x - 1,у + 1) =у + 2 ∧ х = у + 2;
х – 1 ≠ у + 1 (x - 1, у + 1) = х.
В обох випадках отримуємо
(x - 1, у + 1) = if x - 1 = у + 1 then у+ 2 else х = х;
(x, (x - 1,y + 1) = if x = (x - 1,у + 1)
then (х -1, у + 1) + 1 else х + 1 = х + 1.
Отже, остаточно одержуємо
(х, у) = if х = у then у + 1 else х + 1.
Перевірку того, що решта функцій теж є нерухомими точками програми Р пропонується виконати як корисну вправу.
Будемо говорити,
що функції
=
1,2,3, - це нерухомі точки функціональної
програми Р.
Таким чином, всі три функції є розв’язком функціонального рівняння, яке визначає програму Р, і виникає питання: а яку з цих функцій потрібно розглядати як результат виконання програми Р?
Якщо порівнювати
між собою функції
то
виявляється, що коли визначена функція
,
то визначені і функції
,
але не навпаки.
В цьому випадку
говорять, що функція
менш
визначена або рівна, ніж функції
і позначають це так:
.
Неважко переконатися
в тому, що відношення
є відношенням часткового порядку і,
отже, можна говорити, зокрема, про
мінімальні і найменші елементи на
множині функцій відносно цього порядку.
Можна показати,
що розглянута вище функція
відносно
даного порядку менша не тільки по
відношенню до функцій
а
й по відношенню до будь-якої нерухомої
точки програми Р. Більше того, можна
показати, що
є єдиною функцією з такою властивістю
і тому
(х,у) називають найменшою нерухомою
точкою (ННТ) для програми Р.
Розширимо відношення на вектор-функції:
f
= (
)
f '
= (
)
⇔
,
.
Найбільш важливою властивістю функціональних програм є властивість, яку висвітлює відома теорема Кліпі [15].
Теорема 3.2.
Будь-яка функціональна програма Р
має єдину найменшу нерухому точку
.
Операційна семантика. Нехай Р = F(f,x) = Е - функціональна програма. Вираз
Е[
:=
,...,
хп := Еп]
означає одночасну
підстановку в функціональний вираз Е
замість змінних
виразів
,і
= 1,2,... ,п. Наприклад, якщо Р-
програма з попереднього прикладу, то
Е = F(x, у) і
Е[х := 1, у := 2] =if (1 = 2) then 2 + 1 else F(1, F(1-1,2 + 1)).
Якщо одержаний вираз спростити, то
Е[х := 1,у := 2] = F(1,F(0,3)).
Позначимо функцію спрощення функціональних виразів через simp.
Станом обчислень функціональної програми називається функціональний вираз, а відношення переходів на функціональних виразах задається за допомогою такого правила:
.
Приклад 3.2. Якщо F(x,y) - функція з попереднього прикладу, то
F(3,1) F(З, F(2,2)) F(3,3) 4,
оскільки
F(3, F(2,2)) = if (3 = F(2,2)) thеп 4 else F(2, F(2,2) + 1));
F(2,2) = if (2 = 2) then (2+1) else F(2, F(1, 3)) = (2 + 1) = 3.
Тоді F(3,F(2,2)) = 4.
Як випливає з наведених прикладів, значенням функціональної програми Р є або константний вираз А, тобто деякий елемент області даних D, одержаний в результаті спрощення (наприклад, А — 4 в розглянутому вище прикладі), або невизначений елемент ┴.
Зв’язок між денотаційною і операційною семантикою встановлює таке твердження.
Теорема 3.3.
Нехай
,
,...,
Ап, В - деякі константні
вирази такі, що simp(
)
=
simp(B)
= b, і = 1,2, ...,n.
Тоді
]р(
,
,
… ,ап)
(
,
А2,...,
)
*
B, де
]р
- значення функції
,
яка визначається програмою Р.
Доведення пропонується як вправа.
Нижче наводиться функціональна програма обчислення НСД двох натуральних чисел.
НСД(x,y) = if (х > у) then НСД(х - у, у) else
if (х < у) then НСД(x, у - х) else х.
Функціональні програми мають одну дуже корисну властивість порівняно з процедурними програмами: функціональні програми не мають побічних ефектів, пов’язаних з використанням глобальних змінних, в той час як процедурні програми схильні до такого роду ефектів. Побічні ефекти часто приводили до того, що великі затратні розробки зазнавали краху, тому що помилки, які виникають з цих причин, не мають стійкого характеру (їх називають “мерехтливими” помилками). Ці помилки проявляються не завжди при роботі програми і тому їх дуже важко ідентифікувати і виправити.
Розглянемо програму (мова програмування - ПАСКАЛЬ), яка має побічний ефект. Наведена нижче програма обчислює значення суми f(1) + f(2), де f - деяка процедура-функція.
program example(output);
var flag : bool;
function f(n : integer) : integer;
begin
if flag then f := n else f := 2n;
flag := not flag
end;
begin
flag := true;
writeln (f(1) + f(2));
writeln (/(2) + /(1))
end;
Результат роботи програми буде для першого виразу 5, а для другого виразу - 4. Оскільки операція додавання комутативна в множині натуральних чисел, то результати повинні були б збігатися. Причина криється в змінній flag, яка є глобальною і змінює значення функції f.
