Для наступного прикладу створюємо базу даних погоди. Для цього створюємо наступний модуль (лістинг коду 1.3).
Лістинг 1.3 – База даних погоди
:-module(weather,[weather/1]).
weather('back_ice').
weather(X);-X=storm;X=frog.
weather(X):-X=hail;X=rain.
Проведемо декілька операцій з базою даних. Та отримаємо такі результати (рисунок 1.6).
Рисунок 1.6 – Результат роботи з базою даних погоди
У другому і третьому предикат weather ми використовуємо уніфікацію з X, однак, в ході будь-яких запитів перший елемент бази веде себе аналогічно іншими. Створюємо базу даних наступного виду (що містить ті ж предикати у формі виключно фактів (лістинг коду 1.4)).
Лістинг 1.4 – База даних погоди
:-module(weather, [weather /1]).
weather(’black ice’).
weather(storm).
weather(fog).
weather(hail).
weather(snow).
weather(rain).
Отримаємо антологічний результат, як і для попереднього прикладу. Іншими словами, механізм вибору різних термів за шаблоном з бази залишає такий же choice point, як і перерахування значень через «;», А «;», «захований»в предикаті такий же, як і «;» на самому верхньому рівні.
Наступним кроком для виконання завдання розглянемо модель послідовного виконання (рисунок 1.7).
Рисунок 1.7 – Модель послідовного виконання
Внесемо модифікацію(рисунок 1.8).
Рисунок 1.8 – Модифікація моделі послідовного виконання
\ = – це операція, зворотна уніфікації. Вона істинна, коли уніфікація повертає false. Замість A \ = B можна писати \ + (A = B), де \ + – це логічне. Format – це предикат форматного виведення, подібний в мові C. Дужки навколо пар (X \ = value, action) вжиті для наочності, пріоритет оператора «,», формує структуру послідовного виконання, один і той же, тому вони, за великим рахунком, не потрібні.
Завдання 2
Дані наступні факти для предикату reys(From, To), який описує існуючі рейси літаків із зазначенням пунктів відправлення та прибуття.
Необхідно створити СПР, яка визначає для двох напрямків (From, To), чи існує можливість долетіти з одного міста до іншого за дві пересадки і повертає негативну відповідь у будь-якому іншому випадку. Визначити, які саме рейси вимагають дві пересадки.
(рисунок 2.1).
Рисунок 2.1– Приклад between / 3
Якщо запустимо даний код та додамо відладку роздруку (лістинг 2.1)
Лістинг 2.1 – Додаємо відладку роздруку
between (1, 4, I),
format("<~d>", I ),
between(2, 3, I),
format("~d ", I),
fail ; true .
Поведінка between залежить від того, вільний чи зв'язаний його третій аргумент (рисунок 2.3). Якщо він пов'язаний, то предикат робить, власне, те, що декларується назвою перевіряє, що arg1 <= arg3 <= arg3. Але, коли вільний, то сам, не чекаючи особливого запрошення, починає «підказувати» всі варіанти вирішення проблеми.
Рисунок 2.3 – Результат прикладу between
Новим кроком спробуємо просунутися у глиб інтернету. WWW являє собою, як ми знаєте, павутину. Насправді, посилання з одного сайту на інший утворюють якийсь граф. Взагалі ми не можемо просунутися досить далеко, так як робите це в ручному режимі – читаєте сторінку, клацаєте на одиночні посилання. Спробуємо реалізувати приклад (рисунок 2.2).
Цілком очевидно, що для того, щоб почати йти треба зробити перший крок.
Лістинг 2.2 – Код з атрибутом href і роздрукуємо всі посилання
site (Site), ! , http_get(Site, Html, [ ]),
xpath(Html , //a(@href), Href),
writeln (Href),
fail ; true .
Посилання можуть вийти двох типів – локальні (на той же сайт) і глобальні. Нас, в першу чергу, будуть цікавити глобальні посилання, так як саме вони забезпечують просування «вглиб». Для того, щоб їх відрізняти від локальних посилань, можна скористатися бібліотечним модулем роботи з URI (рисунок 2.4).
Рисунок 2.4 – Скористаємося бібліотечним модулем
Рисунок 2.5 – Результат завантаження бібліотек
Може вийти досить змішаний список. У ньому можуть бути присутніми посилання на різні сторінки одного і того ж сайту. Можна зменшити трохи кількість інформації, замінити посилання на сторінки однієї єдиної посиланням на корінь сайту. Скористаємося предикатом uri_components / 2. З його допомогою можна як розібрати URI, так і зібрати, в залежності від напрямку потоку даних (рисунок 2.6).
Рисунок 2.6 – Результит викориcтання uri
По суті, в першому випадку, ми отримуємо те, що в інших мовах називається record (зверніть увагу її ім'я збігається з ім'ям предиката, в цьому немає особливого сенсу, просто так захотіли розробники бібліотеки, ймовірно, для того, щоб не плодити нових імен). Для доступу до структури uri_components/5 можна було б використати (банальну) уніфікацію в поєднанні з анонімними змінними.
Рисунок 2.7 – Уніфікація в поєднанні з анонімними змінними
До речі, ми використовували анонімну змінну «_» – це особлива змінна, єдина у своєму роді. Скільки б _ не зустрічалася, вона завжди означає нову змінну, анонімну (спробуйте write ((_, _, _)).) У Пролозі вона використовується, в залежності від напрямку руху або щоб проігнорувати результат, або, щоб передати в предикат незв'язану змінну.
Однак, повертаючись до останнього прикладу. Залежність від арності і позиційна прихильність давно вважаються mauvais ton. Та ж бібліотека uri надає предикат доступу до структури (рисунок 2.8)
Рисунок 2.8 – Використання uri_data
Тобто, як ми зрозуміли, в нашому випадку (рисунок 2.9).
Рисунок 2.9 – Результат роботи прикладу
Це дає нам можливість написати наступний предикат (лістинг 2.3).
Лістинг 2.3 – Написання наступного предикату
site('www.sinoptik.ua').
site('www. novy.tv').
site('www. kinopoisk.ru').
page_link(Page, Link_To_Site) :-
http_get(Page, Html, []), xpath(Html, //a(@href), Href), uri_is_global(Href),
uri_components(Href, Comps),
uri_data(scheme, Comps, http),
uri_data(authority, Comps, Link_To_Site).
page_links(Page, Site_List) :-
findall(Link, page_link(Page, Link), Link_List),
sort(Link_List, Site_List).
Логічний зміст page_link / 2 наступний: предикат істен тоді і тільки тоді, коли сторінка Page містить посилання на сайт Link_To_Site. Допустимі напрямку руху відзначені в коментарі. + Page означає, що сторінка повинна подаватися на вхід (іншими словами, Page повинна бути вже пов'язана), а Link_To_Site допускає будь-які форми уніфікації (іншими словами, ми можемо давати готове ім'я на перевірку або отримувати список посилань за допомогою одного і того ж предиката). Комментарій is nondet – це попередження програмісту про те, що предикат може залишати choice point (недетермініровані предикати вимагають або циклів або відсікань; навіть якщо ми подаємо обидва аргументи пов'язаними, ми можемо отримати стільки рішень true, скільки разів на сторінці буде зустрінута посилання на Link_To_Site).
Рисунок 2.10 – Результат роботи прикладу
