Программа, которая модифицирует сама себя

В качестве последнего примера использования предикатов "assert" и "retract" рассмотрим программу, которая работает с базой данных, содержащей сведения о терминалах. При выполнении "изменения" программа запрашивает у пользователя информацию и осуществляет обновление базы данных.

терминал (vt220, подсоединен).

терминал (tvi 950, подсоединен).

терминал (beehive, подсоединен).

терминал (рс, отсоединен).

терминал (wyse, подсоединен).

изменить_терминал:—

write ('терминал?'),

read (Т),

write ('новое состояние? '),

read (С),

retract (терминал (Т, -)),

assert (терминал (Т, С)).

% пример сеанса работы с программой

|? - терминал (vt220. Состояние).

Состояние = подсоединен

|?— изменить_терминал.

терминал? vt220.

новое состояние? отсоединен.

да

|?—терминал (vt 220, Состояние).

Состояние = отсоединен

3. 8. Компараторы

X = Y Этот предикат унифицирует аргументы. Если оба аргумента конкретизированы, то предикат проверяет, равны ли они. Если один аргумент конкретизирован, а другой — нет, то неконкретизированный аргумент получает значение конкретизированного. Если аргументами служат неконкретизированные переменные, то они обе становятся одной и той же переменной.

X = = Y Этот предикат успешен, если его аргументы будут в точности одинаковы. Он потерпит неудачу с двумя разными неконкретизированными переменными.

Примеры:

|? - Х = = X.

да

|? - X = = Y.

нет

|?— Х== альфа.

нет

|?— альфа = = альфа.

да

Х\== Y Этот предикат потерпит неудачу, если оба его аргумента будут в точности одинаковы. В противном случае предикат окажется успешным.

Примеры:

|? -альфа \== бета.

да

\? -Х\== альфа.

да

|? -альфа \== альфа.

нет

3. 9. Прочие встроенные предикаты

S =.. L Предикат " =.." можно назвать преобразователем*). S -это терм, а L - список. Данный предикат преобразует список в терм или наоборот. Если длина списка будет больше единицы, то этот предикат превратит список в структуру, причем первый элемент списка станет именем структуры, а все остальные элементы списка станут аргументами структуры.

Примеры:

|? - отец (джордж, лео) =.. Список.

Список = [отец, джордж, лео]

|?— S =.. [рейс, 450, сан_франциско, лос_анжелес].

S = рейс (450, сан_франциско, лос_анжелес)

name (X, L) Здесь Х — атом, а L — список кодов символов в соответствии с кодовой таблицей ascii. Если Х конкретизирована, то предикат "name" ("имя") порождает из этого атома список кодов символов. Если L конкретизирована, то предикат "name" создает константу из списка кодов L.

Примеры:

|? - name (help, L).

L= [104, 101, 108, 112]

|? -name (X, [103, 101, HI, 114, 103, 101]).

X=george

|? - name (X, [50, 51, 52]), integer (X).

Х= 234

Заметьте, что предикат "name" можно употреблять для преобразования целого числа или числа с плавающей точкой в коды символов и наоборот.

Программа ввода "вводполя"

Предикат ввода "read" обладает ограниченными возможностями, поскольку он может считывать только один терм языка Пролог, за которым следует точка. Встроенный предикат "name" позволяет написать более гибкую программу ввода. Программа "вводполя" считывает все символы вплоть до начала Новой строки. У программы "вводполя" имеется один аргумент — неконкретизированная переменная, которая будет конкретизирована любым значением, вводимым пользователем. Если первым символом, который ввел пользователь, является признак перехода к новой строке (пользователь просто нажал клавишу return), то аргумент программы "вводполя" останется неконкретизированным.

В программе "вводполя" для ввода списка символов используется процедура "вводсимв". В процедуре "вводсимв" применена схема считывания с опережением на один символ. Первым аргументом этой процедуры является предыдущий считанный символ, а возвращает она через второй аргумент список символов. Если первым аргументом процедуры будет символ с ascii-кодом 10 (признак перехода к новой строке), то она возвратит пустой список. В противном случае процедура воспользуется предикатом "getO" для считывания еще одного символа. Процесс будет продолжаться рекурсивно, что позволит считать остальные символы строки.

% -

вводполя (Поле):—

getO (С), % если С - признак перехода

( С = 10 % к новой строке, то оста-

% вить неконкретизированной

% переменную Поле.

вводсимв (С, Список), % считать символы вплоть до

% признака перехода к но-

% вой строке

name (Поле, Список) % преобразовать Список в

),!. %атом

% возвратить пустой список, если предыдущим символом является приз-

% нак перехода к новой строке

% + -

вводсимв (10, []): -!.

вводсимв (С, [С | Остаток]): -

getO (С 1), вводсимв (С 1, Остаток),!.

| ?-вводполя (X).

фрэнк смит % пользователь вводит значение

Х ='фрэнк смит'

|? - вводполя (X).

% пользователь нажимает клавишу return

Х=_1

clause (H, В) Здесь Н должна быть конкретизирована заголовком фразы. Предикат "clause" ("фраза") найдет среди фраз текущей программы такую фразу, заголовок которой унифицируется с Н, и выполнит унификацию В с телом этой фразы. Если фраза является фактом, то В будет конкретизирована словом «true».

Примеры:

путешествие (амтрак, нью_йорк, бостон, поезд).

можно_путешествовать (ГородА, ГородВ): -

путешествие (_, ГородА, ГородБ, _),

путешествие (_, ГородБ, ГородВ,_).

|? - clause (путешествие (амтрак, Y, Z, поезд), В).

Y = нью_йорк

Z = бостон

В = true

|? -clause (можно_путешествовать (X, Y), B).

Х=_0

Y=_1

В = (путешествие (_2, _0, _3, _4), путешествие (_5, _3, _1, _6))

repeat Этот предикат генерирует бесконечное число циклов повторения поиска с возвратом. Он эквивалентен такому определению:

повторить.

повторить:— повторить.

Напишем запрос, который целиком считывает файл "ар. data", состоящий из термов, и выводит каждый терм на экран:

|? - see ('ар. data'), % открыть входной файл

repeat,

read (R), % считать терм

write (R), % обработать терм

R = end_of_file, % проверка, нет ли конца файла

!,

seen. % закрыть файл

Нормальный выход из этого цикла может произойти только при выполнении условия « R = end_of_file» (т. е. по концу файла). При этом предикат "сократить" не позволит интерпретатору вернуться назад.

findall (E, Q, L) Встроенный предикат "findall" ("найти_все") имеется во многих версиях языка Пролог. Этот предикат вычисляет все ответы на запрос и возвращает их в виде списка. Здесь L — список ответов, а Е - спецификация формы элемента списка L, которая может представлять собой любой вид терма. Q — это запрос. Пусть имеется база данных "язык/1" (см. разд. 3. 7). Построим при помощи предиката "findall" список всех перечисленных там языков:

|? - findall (L, язык (L), Список).

Список = [итальянский, немецкий, японский, французский, английский]

Таким образом, действие предиката "findall" заключается в переводе одной формы представления информации (факты) в другую (списки).

В следующем примере параметром Е является структура «обслуживает (Компания, Б)», которая строится на основании данных, собранных запросом «путешествие (Компания, А, Б, поезд)».

путешествие (амтрак, нью_йорк, бостон, поезд).

путешествие (ндж_транзит, нью_йорк, принстон, поезд).

путешествие (амтрак, бостон, портленд, поезд).

путешествие (грейхаунд, бостон, портленд, автобус).

путешествие (амтрак, нью_йорк, Вашингтон, поезд).

путешествие (пиплз, нью_йорк, Вашингтон, самолет).

путешествие (пиплз, бирлингтон, нью_йорк, самолет).

|? - findall ( обслуживает (Компания, Б),

путешествие (Компания, А, Б, поезд),

L).

L = [ обслуживает (амтрак, бостон),

обслуживает (ндж_транзит, принстон),

обслуживает (амтрак, портленд),

обслуживает (амтрак, Вашингтон)];

нет

Примечание: текст одного из вариантов предиката "findall" приведен в приложении II. В некоторых версиях языка Пролог есть встроенные предикаты "bagof" и "setof", используемые для аналогичных целей.

Соседние файлы в папке Гл.0,1,2,3,4,5,Предисловие