
- •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. Стиль програмування.
8.2.2.Підрахунок кількості елементів.
Зараз ми розглянемо як можна визначити кількість елементів у списку, іншими словами, підрахуємо довжину списку. Можна дати наступне рекурсивне визначення довжини списку :
Довжина пустого списку [ ] рівна 0.
Довжина непустого списку рівна 1 плюс довжина його хвосту.
В Пролозі цей алгоритм можна реалізувати за допомогою двох фраз:
domains
list = integer*
predicates
length_of(list, integer)
clauses
length_of([], 0).
length_of([_|T], L) :- length_of(T, TailLength),
L = TailLength + 1.
Поглянемо спочатку на другу фразу. Так, length_of [_ ,|T] порівнює любий непустий список, прив`язуючи T до хвосту списка. Значення голови не суттєво, ЕОМ буде її рахувати як один елемент.
Так ціль
goal: length_of([1,2,3], L) ,
використовуючи другу фразу, зв'яже з T значення [2,3]. Наступний крок - обчислити довжину T. Коли це зроблено, не важливо як, тоді TailLength отримає значення 2, і ЭВМ потім зможе добавити до неї 1 та прив'язати L до 3.
Для того, щоб визначити довжину [2,3] рекурсивно викличеться length_of([2,3], TailLength. Ця ціль зрівнює другу фразу, прив'язуючи - [3] до T . Тепер виникає задача, як знайти довжину [3], яка дорівнює 1, а потім добавить 1, щоб отримати довжину [2,3], яка буде 2. І так далі і тому подібно. Тому length_of знов викличе себе рекурсивно, щоб отримати довжину списку [3]. Хвіст для [3] рівний [ ], тому T зв'яжеться з [], і задача зараз полягає в тому , щоб знайти довжину [], потім додати до неї 1, і знайти довжину [3]. На цей раз це зробити легко. Ціль length_of([], Tail Length зрівнюючи першу фразу, зв'яже TailLength з 0. Тепер ЭОМ може додати 1 до неї, отримуючи довжину списку [3] і повернутися до фрази виклику. Яка , в свою чергу , прибавить знову 1 , отримуючи довжину [2,3] і повернеться до фрази, яка викликала її; ця початкова фраза прибавить знову 1 , знаходячи довжину списку [1,2,3].
Малюнок 8.1 показує дійсний процес обчислення довжини списку. Ми використали тут різні змінні, щоб підкреслити той факт, що змінні з одинаковими іменами в різних фразах , або різних викликах однієї і тієї ж фрази, будуть різними.
length-of([1,2,3],L1)
length-of([2,3],L2)
length-of([3],L3)
length-of([],0)
L3 = 0 + 1 = 1
L2 = L3+ 1 = 2
L1 = L2+ 1 = 3
Мал.8.1.Обчислення довжини списку.
8.2.3.Іще один варіант підрахунку довжини списку.
Як ви бачите, предикат length_of не є хвостовою рекурсією. Чи можливо створити рекурсивно-хвостовий предикат визначення довжини списку? Можливо, але для цього потрібно прикласти певні зусилля.
Проблема з length_ of заключається в тому, що ми не можемо обчислити довжину списку, пока не підрахуємо довжину хвоста. Та все ж така можливість мається. Використаємо предикат з трьома аргументами.
- Перший є список, котрий ЕОМ буде читати при кожному виклику, поки він не стане пустим.
- Другий є вільним аргументом, який в кінці роботи буде визначати результат.
- Третій є лічильником, котрий починається з 0 і збільшується з кожним викликом. В кінці лічильник можна уніфікувати з незв'язаним результатом.
Наступна програма реалізує наш підхід.
domains
list = integer*
predicates
length_of(list, integer, integer)
clauses
/* * * * * * * * * * * * * * * * * * * * * * *
* Якщо список пустий, тоді уніфікувати другий *
* результуючий аргумент предикату з третім *
* аргументом - лічильником *
* * * * * * * * * * * * * * * * * * * * * * */
length_of([], Result, Result).
/* * * * * * * * * * * * * * * * * * * * * * * * * * *
* В іншому випадку, додати 1 до лічильника і рекурсивно *
* зв`язати результат цього кроку з результатом в майбутньому. *
* * * * * * * * * * * * * * * * * * * * * * * * * * */
length_of([_|T], Result, Counter):- NewCounter=Counter + 1,
length_of(T, Result, NewCounter).
Запит до цієї програми повинен також мати вмонтований предикат друку. Наприклад:
goal : length_of([1,2,3], L, 0), /* починає з значенням Counter = 0 */
write(L), nl.
Остання версія програми length_of більш складна і менш логічна.