В наступній програмі перша фраза досліджує голову списку. Якщо голова списку співпадає з іменем, яке ми шукаємо, тоді можна зробити заключення, що ім`я належить списку. Тому, що в даному випадку хвіст списку нас не цікавить, він характеризується анонімною змінною. Якщо ж імені немає в голові, тоді його потрібно рекурсивним чином шукати в хвості списку. За це і відповідає друга фраза програми.
Domains
namelist = name*
name = symbol
Predicates
Is_a_member_of(name, namelist)
Clauses
is_a_member_of(Name,[Name|_]).
is_a_member_of(Name,[_|Tail]):- is_a_member_of(Name,Tail).
8.3.Використання одного й того ж предикату для вирішення різних задач.
Особливість ПРОЛОГУ заключається в тому, що коли ви будуєте предикат для вирішення якоїсь конкретної задачі, то він може використовуватись і для вирішення зовсім інших задач. Продемонструємо це на наступному прикладі. Побудуємо предикат конкатенації двох списків з трьома аргументами:
Append(List1,List2,List3)
де List3 є результуючим списком. Використаємо наступний рекурсивний алгоритм.
Якщо перший список пустий, тоді результуючий список буде рівний другому списку (append([],List2,List2)). Інакше, головою третього списку становиться голова першого списку, а хвостом - залишок першого списку і другий список.
Прийдемо до програми:
Domains
integerlist = integer*
Predicates
Append(integerlist, integerlist, integerlist)
Clauses
append([], List, List).
append([X|L1], List2, [X|L3]):-
Append(L1, List2, L3).
Якщо ми задамо ціль
goal: append([1,2],[3,4]), тоді отримаємо список [1,2,3,4].
З іншого боку, розглядаючи предикат append з декларативної точки зору, ми визначили відношення між трьома списками. Таке відношення буде вірним і у випадку, коли буде відомий тільки третій список. Наприклад,
goal: append(L1,L2,[1,2,4] дасть результат
L1 = [], L2 = [1,2,[1,2,4]
l1 = [1], l2 = [2,4]
...
Також ви можете використати предикат append, щоб визначити який із списків можна додати до списку [3,4], щоб отримати список [1,2,3,4]. Наприклад, задавши
goal: append[L1,[3,4],[1,2,3,4] матимемо L1 =[1,2]
Предикат append визначає відношення між множинами на вході і виході. Тому відношення може використовуватись різними способами. Задаючи таке відношення, ви можете запитати:
Який вихід відповідає даному входу? або ж
Який вхід відповідає даному виходу?
Статус аргументів даного предикату при виклику, базується на зразку потоку даних. Предикат append може обробляти любий зразок потоку даних.
Але не всі предикати Прологу можуть бути викликані різними зразками потоку даних. Якщо фраза Прологу може оброблятись різними зразками потоку даних, тоді її називають інвертованою фразою. Зазначимо, що використання інвертованих фраз додає потужності вашим предикатам.
8.4. Знаходження зразу всіх розв`язків.
Як ми вже зазначали раніше, для задання ітераційних процесів в Пролозі, використовується бектрекінг, або ж рекурсія. Рекурсія - більш потужніший засіб. Вона, використовуючи апарат формальних аргументів, може передавати інформацію від одного виклику до іншого. Іншими словами, рекурсія дозволяє відсліджувати часткові результати, аналізувати лічильники.
Але іноді виникає проблема, яку важко вирішити і за допомогою рекурсії. Нехай вам потрібно знайти зразу всі розв`язки задоволення цілі, як частини єдиної складної структури даних. Пролог вирішує дану проблему за допомогою предикату findall, який приймає ціль як один із своїх аргументів, збираючи всі розв'язки цієї цілі в один список.
Предикат findall має три аргументи:
- Перший аргумент VarName визначає який з аргументів в зазначеному предикаті потрібно відібрати в список;
- Другий, Мypredicate, визначає предикат, з якого будуть збиратись значення;
- Третій аргумент, ListParam, з змінною, якою позначається список значень, зібраних через перебір з поверненнями.
Зазначимо, що попередньо повинна бути визначена область, до якої повинні належати значення ListParam.
Наступна програма використовує findall, для визначення середнього віку групи людей.