Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
LP_5(Списки)).doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
201.73 Кб
Скачать

7.2.2.4 Вычеркивание элементов списка

Напишем программу удаления всех вхождений указанного элемента из заданного списка. Она должна иметь три параметра: Входной список, элемент для удаления и результирующий список.

Все должно работать так

? - delete_all([a, b, a, c, a, d], a, Результат).

Результат = [b, c, d]

? - delete_all([a, b, a, c, a, d], b, Результат).

Результат = [a, a, c, a, d]

? - delete_all([a, b, a, c, a, d], prolog, Результат).

Результат = [a, b, a, c, a, d]

Вот три предложения, реализующие действия этой программы

delete_all([],Item,[]).

delete_all([Item|After],Item, R):- delete_all(After,Item,R).

delete_all([NoItem|After],Item,[NoItem|R]):-

delete_all(After,Item,R).

7.2.2.5 Замена элементов списка

Напишем программу, заменяющую все вхождения одного элемента в списке на другой элемент.

Она должна иметь четыре параметра: исходный список, элемент для поиска, элемент чтобы его заменить и список результата.

Вот - некоторые примеры ее работы

? - replace_all([a,b,a,c,a,d], a, яша, Result).

Result = [яша, b, яша, c, яша, d].

? - replace_all([a, b, a, c, a, d], b, foo, Result).

Result = [a, foo, a, c, a, d]

? - replace_all([a,b,a,c,a,d],prolog,logic,Result).

Result = [a, b, a, c, a, d]

Вот текст процедуры.

replace_all([],Item,ReplItem,[]).

replace_all([Item|After],Item,ReplItem,[ReplItem|R]):-

replace_all(After,Item,ReplItem,R).

replace_all([NoItem|After],Item,ReplItem,[NoItem|R]):-

replace_all(After,Item,ReplItem,R).

7.2.2.6 Проверка, является ли аргумент списком.

is_list33([]).  is_list33([_|X]) :- is_list33(X). 

Успешен, если аргумент - список, то есть заканчивается пустым списком [].

?- is_list33([карась, сладкий, маленькая]).

true.

?- is_list33([карась | сладкий]).

false.

7.2.2.7 Подсчет количества элементов в списке.

length33(List,N) возвращает в N количество элементов из которых состоит список List.

Это правило может выглядеть так:

length33([], 0).

length33([_| Lt], N) :- length33(Lt, M), N is 1+M.

Пример его работы на SWI-Прологе.

?- length33([p,q,r,s,t],N).

N = 5.

7.2.2.9 Вывод списка на дисплей.

writelist_v([]).

writelist_v([H|Lt]):- write(H), nl, writelist_v(Lt).

Таким образом список выдается вертикально.

?- writelist_v([красный, синий, голубой]).

красный

синий

голубой

true.

Чтобы вывести его горизонтально, надо в приведенном правиле заменить nl на write(' ').

writelist_h([]).

writelist_h([H|Lt]):- write(H), write(' '), writelist_h(Lt).

?- writelist_h([красный, синий, голубой]).

красный синий голубой

true.

7.3 Задачи на списках

7.3.1 Нахождение путей в связанном неориентированном графе

Рассмотрим сначала следующий связанный ориентированный граф:

Дугу, ведущую из вершины X в вершину Y, представим фактом: edge(X,Y).

Тогда весь (ориентированный) граф запишется так:

e dge(1,2).

edge(1,4).

edge(1,3).

edge(2,3).

edge(2,5).

edge(3,4).

edge(3,5).

edge(4,5).

Это будут наши факты. Но чтобы задать, неориентированный граф надо дополнительно задать те же дуги, но строго в обратном направлении. Для чего мы могли бы добавить еще восемь предложений для edge. К примеру, наряду с уже написанным edge(1,2) для вершин 1 и 2 добавить еще и edge(2,1).

Но лучше ввести новый предикат и для него использовать правила для двух встречных соединений, для которого не важен порядок соединения, а лишь только его наличие:

connected(X,Y) :- edge(X,Y).

connected(X,Y) :- edge(Y,X).

А если обратить внимание на использование операции дизъюнкции ';' при написании правил Пролога, то наш предикат получится короче:

connected(X,Y) :- edge(X,Y) ; edge(Y,X). (1)

Итак, построение исходного графа закончено.

Обратимся к самой программе.

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

Нам нужно написать программу path(S,F,P), где

S – стартовая вершина, F – вершина, оканчивающая путь, а переманная P – это сам путь, который разумно представить в виде списка проходимых вершин.

Например, для вершин 1 и 5 должен получиться результат:

?- path(1,5,P).

P = [1,2,5] ;

P = [1,2,3,5] ;

P = [1,2,3,4,5] ;

P = [1,4,5] ;

P = [1,4,3,5] ;

P = [1,4,3,2,5] ;

P = [1,3,5] ;

P = [1,3,4,5] ;

P = [1,3,2,5] ;

false.

Создадим первое правило, которое будет гласить: «путешествие от A до B по пути P получается, если A и B связаны дугой».

travel(A,B,P,[B|P]) :- connected(A,B). (2)

Форма [B|P] означает, что построение результирующего пути (списка) получается добавлением к уже существующему пути P последней вершины B, причём она должна добавляется спереди, потому что один элемент в список по-другому добавит сложно.

Поскольку ответом должен быть именно найденный путь, скажем, Path, то наша процедура, лучше будет выглядеть так:

path(A,B,Path) :- travel(A,B,[A],Path). (3)

Форма [A] необходима из-за желания получить в ответе именно список, а A - это первый, обязательный его элемент, поскольку с него должен начинаться путь.

Проба программы для соседних вершин:

[trace] 4 ?- path(1,4,Path).

Call: (6) path(1, 4, _G468) ? creep

Call: (7) travel(1, 4, [1], _G468) ? creep

Call: (8) connected(1, 4) ? creep

Call: (9) edge(1, 4) ? creep

Exit: (9) edge(1, 4) ? creep

Exit: (8) connected(1, 4) ? creep

Exit: (7) travel(1, 4, [1], [4, 1]) ? creep

Exit: (6) path(1, 4, [4, 1]) ? creep

Path = [4, 1] ;

Redo: (9) edge(1, 4) ? creep

Fail: (9) edge(1, 4) ? creep

Redo: (8) connected(1, 4) ? creep

Call: (9) edge(4, 1) ? creep

Fail: (9) edge(4, 1) ? creep

Fail: (8) connected(1, 4) ? creep

Fail: (7) travel(1, 4, [1], _G468) ? creep

Fail: (6) path(1, 4, _G468) ? creep

false.

Или без trace

?- path(4,5,P).

P = [5, 4] ;

false.

Получается, что путь ведёт в обратную сторону. Значит, надо добавить “переворачивающий” предикат reverse, и изменить правило (3):

path(A,B,Path) :-

travel(A,B,[A],Q), (4)

reverse(Q,Path).

Теперь верно, что

?- path(1,2,P).

P = [1, 2] ;

false.

?- path(4,5,P).

P = [4, 5] ;

false.

Перейдем к рекурсии по графу и допишем ещё одно утверждение для случая несмежных вершин:

travel(A,B,Visited,Path):- connected(A,C),

C \== B,

\+ member(C,Visited),

travel(C,B,[C|Visited],Path).

Третий параметр - переменная Visited скрывает за собой список вершин того пути, который уже пройден по графу

Смысл добавленного правила таков

Переход от А до B, может быть получен при условии,

(далее комментарий по каждой строке)

  • что А связана с некой вершиной C,

  • причем такой вершиной С, которая не совпадает с вершиной B,

  • и С не находится в уже пройденной части пути,

  • и при этом от С можно продолжить поиск пути до конечной B.

Выбрасывание пройденных вершин гарантирует, что программа не будет зацикливаться.

Таким образом, наша программа для конкретно заданного графа в итоге получает такой вид:

edge(1,2).

edge(1,4).

edge(1,3).

edge(2,3).

edge(2,5).

edge(3,4).

edge(3,5).

edge(4,5).

connected(X,Y) :- edge(X,Y) ; edge(Y,X).

travel(A,B,P,[B|P]) :- connected(A,B).

path(A,B,Path) :-

travel(A,B,[A],Q),

reverse(Q,Path).

travel(A,B,Visited,Path):- connected(A,C),

C \== B,

\+ member(C,Visited),

travel(C,B,[C|Visited],Path).

Программа её была уже показана в начале (!!!)

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]