Pp(L,0),

при цьому початкове значення лічильника колонок встановлюється рівним 0. Перше твердження предиката рр обробляє спеціальний випадок — коли перший аргумент є списком. Якщо це так, то необхідно встановити нову колонку, збільшивши лічильник надеяке число (в нашому випадку 3). Потім ми повинні надрукувати за допомогою рр голову списку, оскільки вона сама може виявитися списком. Далі треба надрукувати всі елементи хвоста списку, розташовуючи кожен елемент в тій же самій колонці. Це якраз і виконує предикат ррх. А предикат ррх використовує рр, оскільки кожен елемент може бути списком. Друге твердження предиката рр відповідає випадку, коли нам необхідно надрукувати що-небудь, що не є списком. Ми просто робимо відступ на вказане число позицій, використовуємо предикат write для друку терма і nl для переходу на новий рядок. Перше твердження для рр також закінчується nl, оскільки друк кожного списку повинен завершитися переходом на новий рядок.

 

 

28. Ввід термів .

Предикат read читає наступний терм, що набирається користувачем на клавіатурі терміналу. Після терма, що вводиться, повинні слідувати крапка «.» і недрукована літера, така як пропуск або |RETURN|. Якщо змінна X не конкретизована, то цільове затвердження read(X) приведе до вводу наступного терма і цей терм буде присвоєний в якості значення змінної X. Як і інші предикати вводу-виводу, з якими ми вже стикалися, предикат read виконується лише один раз. Якщо у момент розгляду цільового твердження read(X) його аргумент конкретизований, то спроба довести узгодженість цього цільового твердження з базою даних викличе читання наступного терма і спробу зіставлення його з аргументом, заданим в read. Узгодженість мети з базою даних залежить від результату цього зіставлення.

29. Вивід літер .

Якщо змінна X має в якості значення деяку літеру (її код ASCII), то ця літера буде надрукована при обробці цільового твердження put(X). Предикат put завжди виконується і не може бути переузгодженим (це приводить до невдачі). В якості «побічного ефекту» put друкує літеру на дисплеї терміналу. Наприклад, ми можемо надрукувати слово hello досить незвичайним способом:

?— put(104), put(101), put(108), put(108), put(111).

Hello

True.

Результатом такої кон'юнкції цілей є друк Прологом літер h, е, l, l, o безпосередньо під запитанням, як показано вище.

there

Інший предикат, з яким ми вже познайомилися,— це tab(X), що друкуює X пропусків (ASCII код дорівнює 32). Зрозуміло, змінній X має бути присвоєне ціле число. Відмітимо, що предикат tab(X) міг би бути означений так:

tab(0) :- !.

tab(N) :- put(32), M is N-1, tab(M).

Тепер ми можемо означити предикат, який ми назвемо друк_рядка. Якщо значенням змінної X є список кодів літер (рядок), то цільове твердження друк_рядка надрукує цей список (рядок), використовуючи put для друку кожного елементу списку.

друк_рядка([]).

друк_рядка([Н|Т]) :- put(H), друк_рядка(Т).

?— друк_рядка("Чарлз V відрікся від престолу у Брюсселі").

Чарлз V відрікся від престолу у Брюсселі

 

30. Ввід літер.

Для вводу літер, що набирають на клавіатурі терміналу, можуть бути використані предикати get0(X) і get(X). Ці предикати завжди узгоджуються з базою даних, якщо їх аргументи неконкретизовані, а спроба повторного узгодження завжди невдала. При обробці цілей, що включають ці предикати, ЕОМ чекає до тих пір, поки користувач не набере на клавіатурі яку-небудь літеру. Вказані предикати трохи відрізняються тим, щo get0(X) присвоїть X будь-яку набрану на клавіатурі літеру незалежно від її виду. Навпаки, get(X) пропустить усі керуючі літери і присвоїть X в якості значення першу друковану літеру. Як відзначалося в темі 2, друкована літера — це літера, яка візуалізується на дисплеї терміналу.

Якщо X вже присвоєно значення, то цільове затвердження get(X) пропустить усі керуючі літери і порівняє друковану літеру, що йде за ними, зі значенням X. Доказ узгодженості цільового твердження залежить від результату цього порівняння. Цільовезатвердження get0(X) порівнює X з наступною літерою і залежно від збігу вважається погодженим з базою даних або ні.

31.Ввід речень

У програмі означається предикат ввести, що має один аргумент. Програма повинна вміти визначати, де закінчується одне слово, що вводиться, і починається наступне. Тому припустимо, що слово складається з декількох букв, цифр або спеціальних літер. Ми розглядатимемо поодиноку лапку і дефіс '-' як спеціальні літери. Літери

. ; : ? !

будуть розглядатися як окремі слова. Усі інші літери є роздільниками між словами. Речення вважається закінченим, коли зустрічається одне із слів '.', '!' чи '?'. В результаті такого означення програма підтримуватиме діалог з користувачем, подібний до наступного:

?— ввести(S).

The man, who is very rich, saw John's watch.

S=[the, man, ',', who, is, very, rich, ',', saw, 'john's', watch, '.']

Програма використовує предикат get0 для вводу літер з терміналу. Затруднення, що пов'язане з предикатом get0, полягає в тому, що якщо літера прочитана з терміналу цим предикатом, то вона «пішла назавжди» і ніяке інше цільове твердження get0 або спроба знову довести цільове твердження get0 не дозволить отримати доступ до цієї літери знову. Тому слід уникати повернення за точку використання get0, якщо ми хочемо уникнути «втрати» літери, яку він читає. Наприклад, наступна програма, яка повинна вводити літери і друкувати їх знову, замінюючи літери а на b (код літери 97 на код 98), не працюватиме:

виконати :- замінити_літеру, виконати.

замінити_літеру :- get0(X)=97, !, put(98).

замінити_літеру :- get0(X), put(X).

Приведену програму у будь-якому випадку не можна вважати хорошою, тому що вона працюватиме вічно. Проте розглянемо ефект спроби довести узгодженість цільового твердження замінити_літеру. Якщо перше правило означення предиката замінити_літеру використовується для читання літери, код якої відмінний від 97, то повернення приведе до того, що буде зроблена спроба скористатися замість нього другим правилом. Проте узгодження цільового затвердження get0(X) в другому правилі приведе до того, що X буде конкретизована наступною літерою. Це пояснюється тим, що доказ початкового цільового затвердження get0 був безповоротним процесом. Таким чином, ця програма насправді не друкувала б всі літери. Вона навіть іноді друкувала б літери а.

Як же програма ввести здолає проблеми повернення при вводі? Відповідь полягає в тому, що програма конструюється таким чином, що вона вводить літери з випередженням на одну літеру, а перевірки літери виконуються правилом, відмінним від правила, в якому ця літера була прочитана. Якщо літера введена в якомусь місці програми і не може бути тут же використана, то вона повертається назад для можливого використання іншими правилами. 8

Відповідно до сказаного предикат для введення одного слова читати_слово насправді має три аргументи. Перший призначений для літери, яка була отримана при останньому виконанні get0 де-небудь в програмі, але яку виявилося неможливим використати в місці її отримання. Другий призначений для атома, який буде створений для прочитаного слова. Останній аргумент призначений для літери, наступної в пропозиції, що вводиться, відразу за прочитаним словом. Для того, щоб визначити, де закінчується слово, необхідно ввести літеру, що йде безпосередньо за словом. Ця літера має бути збережена, тому що вона може виявитися першою літерою іншого слова.