Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lab_2_n.doc
Скачиваний:
16
Добавлен:
12.09.2019
Размер:
78.85 Кб
Скачать

2.2. Определение длины списка

Теперь посмотрим, как можно выяснить, сколько в списке элементов. И вообще, что такое длина списка? Вот простое логи­ческое определение:

Длина [] = 0.

Длина любого другого списка = 1 + длина его хвоста.

/* Это иллюстрация , а не фрагмент программы */ длина([1,2,3],L1).

длина([2,3],L2).

длина([3],L3).

длина([],0).

L3=0+1=1

L2=L3+1=2

L1=L2+1=3

Проблема с предикатом "длина" заключается в том, что мы не сможем вычислить длину списка до тех пор, пока не будет вычис­лена длина хвоста. Но, оказывается, эту проблему можно решить. Для этого нужен предикат с тремя аргументами:

- один - это список, который машина будет сводить на нет, по­ка он не станет пустым (как и в предыдущем случае);

- второй - свободный аргумент, который будет в конечном счете содержать результат (длину);

- третий - счетчик, начинающийся с 0 и заканчивающийся при каждом вызове.

Когда список в конце становится пустым, счетчик унифициру­ется с не связанным (до сих пор) значением результатом.

2.3. Модификация списка

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

На естественном языке это звучит так:

| Чтобы добавить единицу ко всем элементам пустого списка,

| нужно сгенерировать другой пустой список. Чтобы добавить

| 1 ко всем элементам любого другого списка, нужно добавить

| 1 к голове и сделать ее головой результата, а затем доба-

| вить 1 к хвосту и сделать его хвостом результата.

2.4. Принадлежность к списку

Предположим, что у нас есть список с именами "Иван", "Петр", "Евгений", "Федор" и мы хотим выяснить, есть ли некоторое имя в данном списке. Другими словами, мы должны определить отношение "принадлежность" двух аргументов: имени и списка имен. Это соот­ветствует предикату принадлежит(имя,список_имен)

Если голова списка - не "Имя", нам нужно проверить, нет ли элемента "Имя" в хвосте списка.

На естественном языке это звучит так :

Имя принадлежит списку, если Имя - первый элемент списка,

или Имя принадлежит списку, если Имя принадлежит хвосту.

2.5. Присоединение одного списка к другому

Построим предикат для присоединения одного списка к другому. Это будет предикат с тремя аргументами :

присоединить(Список1,Список2,Список3)

Здесь Список1 и Список2 объединяются в Список3. Снова мы используем рекурсию .

Если Список1 пуст, то результатом будет Список2: присоединить([],Список2,Список2).

Если же Список1 не пуст, то первый и второй можно объединить, создав Список3, если голову первого списка сделать головой третьего. Хвост третьего списка - это C3. Он составляется из остатков первого и всего второго списка.

Предикат "присоединить" работает следующим образом: Пока Спи­сок1 не пуст, рекурсивное правило переправляет по одному эле­менту в Список3. Когда Список1 пуст, первое выражение гаранти­рует, что Список2 присоединится к концу результирующего списка.

2.6. Нахождение всех решений сразу

В отличие от поиска с возвратом, при рекурсии можно пере­давать информацию (через аргументы) из одного рекурсивного вы­зова в другой. Благодаря этому рекурсивная процедура по мере выполнения может отслеживать промежуточные результаты или вести

счетчик.

Но есть и у поиска с возвратом свойство, которым не обла­дает рекурсия, а именно, находить все альтернативные решения для заданной цели. Так что можно оказаться в затруднительном положении: нам нужны все решения для цели, но все они нам нужны сразу, как часть одной составной структуры данных.

В Visual-Прологе предусмотрен выход из этого положения. Встроенный предикат findall берет цель как один из своих аргу­ментов и собирает все решения в один список. У findall три аргумента:

- первый аргумент, "ПеремИмя", указывает, какой аргумент в заданном предикате должен собираться в список;

- второй аргумент, "мой_предикат", указывает предикат, значе­ния из которого должны собираться;

- третий аргумент, "ПарамСписка", представляет собой перемен­ную для хранения списка, собираемого в процессе поиска с воз­вратом значений; заметим, что для значений "ПарамСписка" должен быть определен пользовательский домен.

Рассмотрим использование findall для вывода среднего воз­раста группы людей:

/* Использование findall */

domains

имя, адрес = string

возраст = integer

список = возраст*

predicates

человек(имя, адрес, возраст)

сум_список(список, возраст, integer)

goal

findall(Возраст, человек(_, _, Возраст), С),

clauses

человек("Sherlock Holmes", "22B Baker Street", 42).

человек("Pete Spiers", "Apt. 22, 21st Street", 36).

человек("Mary Darrow", "Suite 2, Omega Home", 51).

Выражение findall в этой программе создает список C, в ко­торый собираются все значения возраста, получаемые из предиката "человек". Если бы нам нужно было собрать список всех, кому 42 года, для этого можно было бы воспользоваться подцелью

findall(Кто,человек(Кто,_,42),Список).

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