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

7.2 Операции на списках

7.2.1 Поиск элемента в списке

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

Реализуем такой алгоритм.

Проверяем первый элемент в списке и, если нет совпадения, повторяем этот процесс на остальной части списка. Такое повторение делается с помощью рекурсии, причем поиск останавливается, когда мы или находим специфический элемент в начале списка или когда пройден весь список.

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

[Head|Tail]

что и составит основу поиска.

Для примера, назвав нашу процедуру membr, проверим, находятся ли яблоки в списке

[груши, помидоры, яблоки, виноград].

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

membr(Item, [Item|Rest]). /* - целевой элемент совпадает с головой списка */

Иначе – он должен быть головой нового списка, полученного из исходного выбрасыванием первого элемента (поскольку он нам не подошел) и с новым списком мы начинаем поиск заново.

membr(Item, [BadHead|Tail]):- membr(Item, Tail).

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

membr(Item, [Item|Rest]).

membr(Item, [BadHead|Tail]):- membr(Item, Tail).

Давайте посмотрим на нашем примере, как работает алгоритм:

? - membr(яблоки, [груши, помидоры, яблоки, виноград]).

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

Тогда надо разбить список следующим образом:

BadHead = груши

Tail = [помидоры, яблоки, виноград]

что по второму правилу дает нам следующую цель запроса:

membr(яблоки, [помидоры, яблоки, виноград]).

Снова мы видим не соответствие яблоки и помидоры, и снова приходится образовывать новый список, отбрасывая «плохую голову», т.е.,

BadHead = помидоры

Tail = [яблоки, виноград]

И вот теперь новая цель: membr(яблоки, [яблоки, виноград]).

наконец удовлетворяет наш запрос.

7.2.2 Создание списков

7.2.2.1 Простейший случай

Чтобы организовать список, например Z, добавлением спереди к списку Y элемента X, надо написать: Z=[X|Y]

Например, возьмем существующий список, скажем List1, и попробуем создать новый список List2, но с новой головой, например атомом prolog:

List2 = [prolog|List1]

И если List1 это список [lisp, c, pascal, basic],то теперь мы получаем список List2, такой: [prolog, lisp, c, pascal, basic],

С помощью унификации это можно проиллюстрировать так:

?- qq(List2) = qq([prolog |[lisp, c, pascal, basic]]).

List2 = [prolog, lisp, c, pascal, basic]

7.2.2.2 Получение списка из двух существующих списков

Воспользуемся встроенным предикатом append, который берет три параметра. Первые два - списки. Предикат использует эти два списка, чтобы произвести третий список, объединяя первые два, присоединяя первый список к голове второго списка.

Например,

?- append([a, b, c], [один, два, три], Rезультат).

Rезультат = [a, b, c, один, два, три]

Теперь попробуем придумать программу, реализующую алгоритм append. Чтобы не путать нашу процедуру со стандартной, её имя запишем как appnd,

Итак, мы сначала пройдем первый список до конца, просматривая каждый элемент и запоминая его. Это мы должны сделать рекурсивно, чтобы потом возвращаясь из рекурсии добавлять эти элементы ко второму списку спереди, но в обратном порядке. Таким образом, последний элемент в первом списке станет первым добавленным ко второму списку.

Надо только оговорить начальное условие добавления

appnd ([],List, List).

Теперь основное работающее правило. Оно перемещает текущий (первый) элемент первого списка на первое место списка резутата.

appnd([Head|Tail], List2, [Head|Result]):-

appnd(Tail, List2, Result).

Посмотрим работу программы на таком примере

? - appnd([a, b, c], [один, два, три], Rезультат).

Для детализации вызовем trace.

[trace] 5 ?- appnd([a, b, c], [один, два, три], Rезультат).

Call: (6) appnd([a, b, c], [один, два, три], _G672) ? creep

Call: (7) appnd([b, c], [один, два, три], _G757) ? creep

Call: (8) appnd([c], [один, два, три], _G760) ? creep

Call: (9) appnd([], [один, два, три], _G763) ? creep

Exit: (9) appnd([], [один, два, три], [один, два, три]) ? creep

Exit: (8) appnd([c], [один, два, три], [c, один, два, три]) ? creep

Exit: (7) appnd([b, c], [один, два, три], [b, c, один, два, три]) ? creep

Exit: (6) appnd([a, b, c], [один, два, три], [a, b, c, один, два, три]) ? creep

Rезультат = [a, b, c, один, два, три].

Итак, используя второе предложение мы сначала уменьшаем запрос до

appnd ([b, c], [один, два, три], Результат)

затем до

appnd ([c], [один, два, три], Результат)

и наконец получаем

appnd ([], [один, два, три], Результат).

что унифицируется по первому предложению

appnd([],[one,two,three],[one,two,three]).

Так как это факт - он заканчивает рекурсию. Тем самым заканчивается последний рекурсивный вызов, что в итоге позволяет вернуться назад, заканчивая и всю рекурсия с получаемым результирующим списком.

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