Списки
Список - это упорядоченный набор объектов - элементов списка, следующих друг за другом. Элементы списка в языке Турбо-Пролог должны принадлежать одному и тому же доменному типу. Объектами списка могут быть: целые числа, действительные числа, символы, символьные строки, структуры.
Совокупность элементов списка заключается в квадратные скобки, а элементы друг от друга отделяются запятыми. Примерами списков являются:
[10,24,1,8,385,0,8]
[0.4,67.43,986.01,914.5]
["ПЕТРОВ П.Р.","ИВАНОВ Б.О.","СИДОРОВ Т.К."]
Список может содержать произвольное число элементов (ограничением является объем доступной оперативной памяти). Список, не содержащий элементов, называется пустым списком и обозначается []. Обработка списков в Турбо-Прологе базируется на разбиении списка на голову - первый элемент в списке и хвост - остальную часть списка. Например, как показано в следующей таблице:
Список |
Голова |
Хвост |
[1,2,3,4] |
1 |
[2,3,4] |
['а'.'b'.'с'] |
'а' |
['bYc'l |
["Курсант"] |
"Курсант" |
[ ] |
[] |
не определено |
не определено |
Наглядным представлением процесса разбиения списка на голову и хвост является графическое представление в виде бинарного (двоичного) дерева. Для списка [1,2,3,4] таким деревом будет следующее:
Для использования списков в Пролог-программе необходимо:
1) В разделе domains описать домен списка. Домен списка задается в формате:
= *
319
Например, домен списка, элементами которого являются целые числа, описывается следующим образом:
domains
list_num=integer*
или
list_num=elem*
elem= Integer
2) Описать работающий со списком предикат в разделе predicates, например:
predicates
num(list_num)
3) Ввести или создать в программе собственно список, т.е. задать список в разделах
goal или clauses.
Пример задания списков в Пролог-программе:
domains
Iist1=symbol*
Iist2=num*
num=integer
predicates
name(list1)
score(list2)
clauses
name(["ПЕТРОВ П.Р.","ИВАНОВ Б.О.","СИДОРОВ Т.К."]).
score([1,3,5,7,9]).
Ниже приведены примеры внешних запросов к программе и получаемые при этом результаты:
Запрос |
Результат |
nате(Х) |
Х= ["Петров П. Р.", "Иванов Б. О.", "Сидоров Т. К."]. |
name([_, Y, _]) |
Y="Иванов Б. О." |
score([A,B,_,_,C]) |
A=1,B=3,C=9 |
При использовании в Пролог-программе списков с произвольным числом элементов используется метод разделения списка на голову и хвост. Этот метод обеспечивает рекурсивную обработку списка. Операция разделения списка на голову и хвост в языке Турбо-Пролог обозначается вертикальной чертой (|) и записывается в виде:
[Head | Tail]
Здесь: Head - переменная для обозначения головы списка; Tail - переменная для обозначения хвоста списка.
Пример 1. Рассмотрим программу распечатки содержимого списка, элементы которого могут быть целыми числами или символьными строками:
domains
Iist1=integer*
320
Iist2=symbol*
predicates
printjist(listl)
print_list(list2)
clauses
print_list([]).
print_list([Head|Tail]):-write(Head),
nl,
print_list(Tail).
goal
print_list([1,2,3,4]).
В данной программе предикат списка print_list() описан для двух типов доменов, поэтому с его помощью могут обрабатываться списки, элементами которых являются как целые числа, так и символьные строки. В программе реализован рекурсивный метод обработки.
Первое правило программы print_list([]) описывает тот факт, что пустой список не нужно печатать, оно же является условием выхода из рекурсии, используемым во втором правиле - печать списка завершается тогда, когда список пуст. Опишем работу программы.
Первоначально аргумент целевого внутреннего запроса printjist([1,2,3,4]) сопоставляется с первым вариантом правила; сопоставление терпит неуспех, так как [1,2,3,4]#[]. Затем выбирается второй вариант правила, здесь сопоставление завершается успехом и выполняется операция - разделения целевого списка на голову и хвост. Результатом ее является означение Head=1 и Tail=[2,3,4]. Далее начинают последовательно выполняться предикаты тела правила.
Первым в теле стоит стандартный предикат write(Head), который выводит на экран значение переменной Head, в данном случае 1, затем выполняется предикат nl - перевод строки и формируется рекурсивный вызов print_list([2,3,4]). Снова обращение к первому варианту правила и снова неуспех - [2,3,4]#[], и снова успешное сопоставление со вторым вариантом и т.д. В конечном итоге переменная Head становится равной 4, a Tail=[], и теперь после рекурсивного вызова printjist([]) сопоставление для первого варианта правила завершится успехом - []=[], этот вариант правила не имеет рекурсии - он является фактом и интерпретируется Прологом как цель достигнута и целевой запрос считается удовлетворенным.
Пример 2. Рассмотрим Пролог-программу подсчета суммы произведений элементов двух векторов X и Y, представленных списками Х=[х1 ,х2,.. .хn] и Y=[y1 ,у2 ,.. ,уn]:
domains
vector=integer*
predicates
prod(vector, vector, integer)
clauses
prod([],[],0).
prod([X|Xs],[Y|Ys],S):-prod(Xs,Ys,Sp),
321
S=X*Y+Sp.
goal
prod([1,2,3],[7,8,9],Rest),white("Rest=",Res).
Предикат prod (...) является трехместным: первый аргумент - список X, второй - список Y и третий - сумма произведений списков. В разделе
clauses описаны два варианта правила для этого предиката.
Первый вариант - утверждение о том, что сумма произведений элементов пустых списков равна 0. Второй вариант правила реализован с использованием хвостовой рекурсии. Выражение S=X*Y+Sp означает, что текущая сумма равна сумме произведений текущих элементов вектора и частичной суммы, полученной на предыдущих шагах вычислений.
Схематично действия при выполнении программы представлены в табл. 24.2. Рекурсивное правило описывает отсроченные вычисления - после рекурсивного вызова остается не выполненным хвост правила, копии которого помещаются в стек до тех пор, пока не произойдет сопоставление с первым вариантом правила. И после того, как это произойдет, частичная сумма Sp" получит значение 0 и все накопленные в стеке хвостовые вычисления будут выполнены в обратном порядке.
Таблица 24.2
Действия при выполнении программы
Вызов предиката |
Подстановки |
Хвостовые вычисления |
Результаты вычислений |
Prod([1, 2,3], [7,8,9], Res) |
X=1, Y=7, Xs=[2,3], Ys=[8,9], Res=S |
S=X*Y+Sp |
S=1*7+43=50 |
Prod([2,3],[8,9],Sp) |
X'=2, Y'=8, Xs'=[3], Ys'=[9], S=Sp |
Sp=X'*Y'+Sp' |
Sp=2*8+27=43 |
Prod([3],[9],Sp') |
X"=3, Y"=9, Xs"=[], Ys"=[], Sp=Sp' |
Sp'=X"*Y"+Sp" |
Sp'=3*9+0=27 |
Prod([],[],Sp") |
Xs"=[],Ys"=[], Sp"=0 |
|
Sp"=0 |
Ниже приведен текст Пролог-программы, в которой реализованы основные операции над списками целых чисел: создание списка, добавление элементов в список, распечатка содержимого списка, удаление элемента из списка, упорядочивание элементов в списке. В программе реализован оконный интерфейс и используется модель общения типа выбора из меню.
domains
Sp1, Sp= integer*
P, U, Z, Z1, N1, N, N2, X, F, P1, Q = integer
predicates
edit_list window(Sp, P) view_list(Sp)
make_list(Sp) delete_list(Sp)
process(X,Sp,P) insert_sort(Sp,Sp)
322
attent_windowcreate(N,Sp,Z)
list(Sp,integer) insert(integer,Sp,Sp) inversion(Sp,Sp)
split(integer,integer,Sp,Sp,Sp)connect(Sp,Sp,Sp)
add_list(Sp) goto(Sp,integer) prt(char,Sp)
qrt(char,Sp) delete(integer,Sp) sound1 sound2
goal
edit_list.
clauses
/*------------------------------------ОСНОВНОЕ МЕНЮ------------------------------------*/
edit_list:-window([],0).
window(Sp,P):-makewindow(1,31)7l"MEHЮ
РЕДАКТОРА",0, 0, 25, 80), cursor(5,15),
write(" СОЗДАНИЕ СПИСКА -1 "),cursor(7,15),
write(" ПPOCMOTP СОДЕРЖИМОГО СПИСКА - 2"),cursor(9,15),
write("КОНКАТЕНАЦИЯ СПИСКА И ЭЛЕМЕНТА - 3"),cursor(11,15),
write("УДАЛЕНИЕ - 4"),cursor( 13,15),
write("УПОРЯДОЧИВАНИЕ СПИСКА ПО ВОЗВРАСТАНИЮ-5"),cursor(15,15),
write("BЫХОД ИЗ РЕДАКТОРА - 6"),cursor(20,10),
write("Введите номер пункта меню:"),sound1,
readint(X),sound(8, 2000), X
process(1 ,Sp,0):-make_list(Sp).
process(1,Sp,1):-make_list([]).
process(4,Sp,0):-attent_window,window(Sp,0).
process(4,Sp,1):-delete_list(Sp).
process(2,Sp,0):-attent_window,window(Sp,0).
process(2,Sp,1):-view_list(Sp).
process(3,Sp,0):-add_list(Sp).
process(3,Sp,1 ):-add_list(Sp).
process(5,Sp,0):-attent_window,window(Sp,0).
process(5,Sp,1):-insert_sort(Sp,Sp1),view_list(Sp1).
process(6,Sp, 1 ):-sound2, exit. process(6,Sp,0):-sound2, exit.
/*------------------------------------СОЗДАНИЕ СПИСКА---------------------------------*/
make_list(Sp):-makewindow(2,48,7," СОЗДАНИЕ СПИСКА",5,5,15,70),
gotowindow(2),cursor(3,8),
write("Количество элементов в списке - "),readint(N),
sound(8,2000),cursor(5,8),
write(" Введите элементы списка: "),Z=1,Sp=[],create(N,Sp,Z).
create(0,Sp,Z):-inversion(Sp,[]).create(N,Sp,Z):-
write(" # ",Z,"-"),readint(U),sound(8,3000),scroll(1,0),cursor(5,32),
N1 =N-1, Z1 =Z+1 ,create(N1 ,[U | Sp],Z1).
inversion([],Sp1 ):-window(Sp1,1).
inversion([H|T],Sp):-inversion(T,[H|Sp]).
323
/*---------------- ОКНО ПРЕДУПРЕЖДЕНИЯ ----------- */
attent_window:-akewindow(3, 64,7, "BHИMAHИE",10,25,5,30),gotowindow(3),nl,
write(" Ваш список пустой !"),sound(50,1 000), readchar(L).
/*-------------------------УДАЛЕНИЕ ---------------------*/
delete_list(Sp):-makewindow(8,48,7,"УДAЛEHИE",5,15,15, 45), gotowindow(8)l
write(""),nl,
write(" СПИСКА - 1 "),nl,
write(" ЭЛЕМЕНТА В СПИСКЕ - 2 "),nl, cursor(7,5),readint(N),delete(N, Sp).
delete(1, Sp):-makewindow(9, 64, 7,"", 10, 23, 5, 30), gotowindow(9),nl,
write("CПИCOK УДAЛEH!"), sound1, readchar(U),window([], 0).
delete(2,Sp):-makewindow(4, 48, 7, " УДАЛЕНИЕ ЭЛЕМЕНТА", 10, 15, 5,45),
gotowindow(4),nl,
write(" Введите номер удаляемого элемента: "),readint(S),N=0,
split(S,N,Sp,Sp1,[H|T]),connect(Sp1,T,Sp3),view_list(Sp3).
split(S, N,[H|T],[H|L1],L2):-N1=N+1,N1
split(S,N, [H|T],L1,[H|L2]):-N1=N+1,split(S,N1,T,L1,L2),N1>=S.
connect([],L,L).
connect([N | L1 ],L2,[N | L3]):-connect(L1 ,L2,L3).
/*------------------- ПРОСМОТР СОДЕРЖИМОГО СПИСКА --------- */
view_list(Sp):-makewindow(5,48,7,"COДEPЖИMOE СПИСКА",5, 1 5, 1 5,45),
gotowindow(5),cursor(10,5),list(Sp,1),readchar(L),window(Sp,1).
list([], N).
list(T,7):-readchar(L),scroll(1 ,0),cursor( 10,5),list(T, 1 ).
list([H|T],N):-N1=N+1,N2=N1*6,write(H), sound(10,3000),
cursor(10,N2),list(T,N1),!.
/* ------- УПОРЯДОЧИВАНИЕ ЭЛЕМЕНТОВ ПО ВОЗРАСТАНИЮ-----------*/
insert_sort( [],[]).
insert_sort([X|Tail],Sorted_list):-insert_sort(Tail,Sorted_Tail),
insert(X,Sorted_Tail,Sorted_list).
insert(X,[Y|Sorted-list],[Y|Sorted_list1]):-X>Y,!,
insert(X,Sorted_list,Sorted_list1).
insert(X,SortedJist,[X|Sorted_list]).
/* ----------- ДОБАВЛЕНИЕ ЭЛЕМЕНТА К СПИСКУ -------- */
add_list(Sр):-makewindow(6,48,7,"ДОБАВЛЕНИЕ К СПИСКУ",5, 15, 15,45),
gotowindow(6), cursor(3,5),
write("B НАЧАЛО СПИСКА - 1 "),cursor(5,5),
write("B КОНЕЦ СПИСКА - 2"),cursor(7,5),
readint(L),sound(8,2000),L
goto(Sp, 1 ):-makewindow(7,48,7, "ДOБABЛEHИE К СПИСКУ",5, 15, 15,45),
gotowindow(7),cursor(5,5),
write("BBEДИTE НОВЫЙ ЭЛЕМЕНТ: "),cursor(5,28),
readint(X),sound(8,3000),connect([X],Sp,Sp1),cursor(7,5),
324
write (" ЕЩЕ - ? < y/n > "),readchar(Z),sound(8, 2000),qrt(Z,Sp1).
goto(Sp,2):-makewindow(8,48, 7,"ДOБABЛEHИEKCПИCKУ", 5, 15,15,45),
gotowindow(8),cursor(5,5),
write("BBEДИTE НОВЫЙ ЭЛЕМЕНТ: "),cursor(5,28),
readint(X), sound(8,3000),connect(Sp,[X], Sp1),cursor(7,5),
write("ЕЩЕ - ? < y/n > "),readchar(Z),sound(8,2000),prt(Z,Sp1).
prt('y',Sp1):-goto(Sp1,2).
prt('n',Sp1 ):-view_list(Sp1).
qrt('y',Sp1):-goto(Sp1,1).
qrt('n',Sp1 ):-view_list(Sp1).
/*------------------------------------------------------------------------------------*/
soundl :-sound(15,1000),sound(15,1500),sound(15,2000).
sound2:-sound(15,2000),sound(15,1500),sound(15,1000).
325
319 :: 320 :: 321 :: 322 :: 323 :: 324 :: 325 :: Содержание
Содержание раздела