
- •1.Пролог - мова логічного програмування.
- •1.1.Загальний огляд мови Пролог.
- •1.2.Переваги і недоліки мови Пролог.
- •1.3.Числення предикатів - математична основа мови.
- •1.4.Побудова теорії деякої області знань.
- •1.5.Від формальної логіки до логічного програмування.
- •1.6.Механізм логічного виведення і керування пошуком.
- •2.Основні концепції прологу.
- •2.1.Факти та правила.
- •2.2.Як змінні отримують свої значення.
- •2.3.Анонімні змінні.
- •2.4.Складні цілі: кон`юнкція та диз`юнкція.
- •2.5.Способи Співставлення.
- •3.Структура програми pdc прологу.
- •3.1.Основні розділи програми.
- •3.2 Стандартні домени.
- •3.3.Синтаксис правила.
- •3.4.Директиви комп`ютеру.
- •3.5.Бектрекінг.
- •3.5.1.Бектрекінг з внутрішньою ціллю.
- •4.Контроль пошуку рішень.
- •4.1.Використання предикату fail.
- •4.2.Відміна бектрекінгу.
- •1.Коли ви знаєте попередньо, що певні варіанти ніколи не дадуть поштовху в знаходженні розв'язку, тоді використання cut(зелений cut) відкидає перегляд альтернативних шляхів.
- •2.Коли логіка програми потребує використання cut для відкидання перегляду альтернативних підцілей, тоді його називають червоним відтинанням.
- •4.3.Предикат not - заперечення як неуспіх.
- •4.4.Труднощі у використанні відтинання і заперечення.
- •4.5.Засоби керування.
- •4.6.Узагальнення.
- •5.Прості та складні об'єкти.
- •5.1 Прості дані.
- •5.1.1. Константи як об'єкти даних.
- •5.2.Складні об'єкти даних і функтори.
- •5.2.1.Уніфікація складних об`єктів.
- •5.2.2.Приклад застосування функторів.
- •5.3.Приклад використання складних об'зктів.
- •5.4.Опис доменів складних об'єктів.
- •5.5.Багаторівневі складні об'єкти.
- •5.6.Приклад, який ілюструє задання структури речення англійської мови.
- •5.7.Опис змішаних складних об'єктів.
- •5.7.1.Аргументи, які можуть мати різний тип.
- •5.7.2 Cписковий тип.
- •5.8.Порівняння складних об`єктів.
- •5.9.Узагальнення.
- •6. Ітерація і рекурсія.
- •6.1.Реалізація ітераційного процесу за допомогою бектрекінгу.
- •6.2.Дії типу до і після.
- •6.3.Застосування бектрекінгу для реалізації циклів.
- •6.4.Рекурсивні процедури.
- •6.5.Використання аргументів в якості параметрів циклу.
- •7. Рекурсивні структури даних.
- •7.1.Структура даних типу дерева.
- •7.2.Обходи дерева.
- •7.3.Створення дерева.
- •7.4.Бінарний пошук на дереві.
- •7.5. Сортування по дереву.
- •7.5. Програмна реалізація лексикографічного впорядкування при символьному вхідному потоці.
- •8. Робота з списками в пролозі.
- •8.1.Рекурсивна сутність списку.
- •8.2.Обробка списків.
- •8.2.1.Друк списків.
- •8.2.2.Підрахунок кількості елементів.
- •8.2.3.Іще один варіант підрахунку довжини списку.
- •8.2.4.Модифікація списку.
- •8.2.5.Належність елемента списку.
- •8.3.Використання одного й того ж предикату для вирішення різних задач.
- •8.4. Знаходження зразу всіх розв`язків.
- •8.5.Складні списки.
- •8.6.Реалізація синтаксичного аналізу за допомогою списків.
- •9. Техніка програмування в пролозі.
- •9.1.Принципи побудови експертної системи.
- •9.2. Макетування: задача маршрутизації.
- •9.3.Пригоди в дивних печерах.
- •9.4. Моделювання апаратних засобів.
- •9.5.Задача про ханойські башні.
- •9.6.Ділення слів на склади.
- •9.7. Задача про n королев.
- •10.Особливі технічні прийоми для професіоналів.
- •10.1.Потоковий аналіз.
- •10.2.Керування потоковим аналізом.
- •10.3. Стиль програмування.
10.3. Стиль програмування.
В цьому розділі ми дамо рекомендації для написання елегантних і ефективних програм на Пролозі.
Для виміру ефективності програми традиційно використовують два параметри: пам`ять і час. В Пролозі на покращання цих оцінок значною мірою впливає відміна хвостової рекурсії.
Розглянемо відомий нам предикат member:
member (X, [X|_]).
member (X,[_|Y]): member (X,Y)
Ітеративна операція перевірки або генерації елементів потрібного списку проводиться рекурсивним чином, тому стекова пам`ять (а відповідно і час виконання) повністю залежить від рекурсії. Припустимо, що предикат demopred описується так:
demopred (X,Y): - ... , member (A,B), test (A), ...
При активізації member спочатку система повинна запам`ятати, що після успішного виконання member керування потрібно передати предикату test. Тому, адреса test повинна бути збережена в стеці. Для кожного рекурсивного звертання до member система запам`ятовує адресу, до якої повинен повернутись предикат member після успішного завертання цього предикату. Враховуючи, що між member (X,[_,Y]):- і рекурсивним зверненням member(X,Y) не має точок повернення до попереднього стану, не має необхідності зберігати адресу member в стеці декілька разів. Достатньо запам`ятати, що після успішного завершення предикату member керування повинно бути передане предикату test. Це і буде відміною хвостової рекурсії. Там, де система не може відмінити рекурсію, програміст може зробити це сам, використовуючи наступні правила.
Правило1. Краще використовувати більше змінних, ніж предикатів.
Це правило знаходиться часто в прямому конфлікті з наглядністю програми. В багатьох випадках декларативний стиль Прологу приводить до гіршого коду в порівнянні з процедурними мовами. Наприклад, якщо ви пишете предикат, для перестановки елементів списку, тоді такий фрагмент коду, як:
reverse(X,Y):- reverse1([],X,Y). /*More efficient*/
reverse1(Y, [], X).
reverse1(X1, [U | X2], Y):- reverse1([U|X1],X2,Y).
пред'являє менше вимог до стеку, ніж використання додатково предикату append:
reverse([],[]).
reverse([U | X], Y):- reverse(X,Y1), append(Y1,[U],Y).
append([],Y,Y).
append([U|X],Y,[U|Z]):-append(X,Y,Z).
Правило 2. При впорядкуванні підцілей в правилі, першими розміщуйте підцілі з найбільш зв'язаними змінними.
Наприклад ви пишете предикат для вирішення системи рівнянь
Х + 1 = 4
Х + Y = 5
використовуючи метод "генеруй_ і_перевіряй":
solve(X,Y):- /*кращій варіант*/
num(X), plus(X, 1, 4),
num(Y), plus(X, Y, 5).
Тоді він буде кращим за наступний фрагмент :
solve(X,Y):- /*гірший варіант*/
num(X), num(Y),
plus(X, Y, 5), plus(X, 1, 4).
Предикати num і plus визначались нами раніше.
Правило 3. Коли не існує рішень, пробуйте перевіряти, що виконання видасть повідомлення про "неуспіх" эффективно.
Припустимо, що ми хочемо написати предикат singlepeak, який перевіряє наявність "піку" серед списку цілих чисел. Іншими словами, числа повинні зростати до одного максимуму, а потім спадати. Для такого предикату виклик
singlepeak ([1,2,5,7,11,8,6,4]).
буде успішним, тоді як виклик
singlepeak ([1,2,3,9,6,8,5,4,3]).
видасть повідомлення "неуспіх".
Наступне визначення для singlepeak не враховує Правило 3, тому що наявність в списку однієї "вершини" проводиться тільки у випадку, коли append розбиває список на різноманітні декомпозиції:
singlepeak(X):- append(X1,X2,X), up(X1), down(X2).
up(_).
up([U,V|Y]):- U < V, up([V,Y]).
down([]).
down([U]).
down([U,V|Y]):- U > V, down ([V|Y]).
Можна також запропонувати варіанти, які враховують настанови Правила3:
singlepeak([]).
singlepeak([U,V|Y]):- U < V, singlepeak([U|Y]).
singlepeak([U,V|Y]):- U > V, down([V|Y]).
down([]).
down([U]).
down([U,V|Y]):- U < V, down ([V|Y]).
або ж варіант:
singlepeak([],_).
singlepeak([U,V|W],up):- U < V, singlepeak([V|W],Up).
singlepeak([U,V|W],_):- U > V, singlepeak([V|W],Down).
Правило 4. Механізм уніфікації повинен виконувати як можна більше роботи.
Наприклад, для перевірки на рівність двох списків можна використати наступний фрагмент програми:
equal([],[]).
equal([U|X],[U|Y]):-equal(X,Y).
aле в цьому немає потреби, тому що предикат
equal(Х,Х)
за рахунок механізму уніфікації виконає всю потрібну роботу.
Правило 5. Для повтору краще використовуйте бектрекінг ніж рекурсію.
Бектрекінг зменшує стекові вимоги. Ідея заключається у тому, щоб використовувати конструкцію типу repeat...fail замість рекурсії. Наприклад, для повторного обчислення деякого предикату process(X,Y) можна використати наступну послідовність предикатів:
run:-readln(X),
process(X,Y),
write(Y),
run.
Але комбінація repeat...fail зменшує необхідність кінечного рекурсивного виклику. Визначивши
repeat.
repeat:-repeat.
ми можемо перевизначити run без рекурсії:
run:-repeat,
readln(X),
process(X,Y),
write(Y),
fail.
Тут, fail примушує Пролог виконувати бектрекінг в repeat, який завжди виконується успішно.
Література.
1. И. Братко. Программирование на языке Пролог для искусственного интеллекта. Москва, Мир, 1990.
2. Дж. Малпас. Реляционный язык Пролог и его применение. Москва, Мир, 1990.