Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
AlgStr / Библиотека / ЛЕКЦИИ / POSIBNIK / ПРОГР НА ПРОЛОГЕ.doc
Скачиваний:
42
Добавлен:
23.03.2015
Размер:
669.7 Кб
Скачать

4.7.2. Використання списків

Списки дуже важливі при програмуванні на ПРОЛОЗі. Списки дозволяють працювати з набором об'єктів довільної довжини як із єдиним цілим. При цьому важливий порядок розташування таких об'єктів у списку. Списки можуть складатися з чисел, символів, рядків, довільних структур і т.ін. Якщо компонентами списку постають списки, то можна говорити про списки списків. Причому ці списки можуть містити елементи різних типів.

Зокрема, списки можуть застосовуватися для подання алгебраїчних матриць:

a b c d

A = e f g h .

i j k l

Тоді на ПРОЛОЗі цю матрицю можна подати у вигляді

A = [ [ a, b, c, d], [e, f, g, h], [i, j, k, l] ].

Щоб використовувати списки в програмі на TP, насамперед необхідно описати списковий доменний тип. Без цього не можна буде визначити жодного предиката бази даних, що зберігає ці списки або програмні предикати, які обробляють списки.

Наприклад, нехай у нас будуть списки трьох виглядів: списки цілих, списки дійсних чисел і списки тварин. Для цього знадобляться такі описи:

DOMAINS

список_цілих = integer *

список_дійсних = real *

список_тварин = symbol *

Як видно, символ “*” застосовується для задання спискового доменного типу.

Тепер необхідно подбати про збереження таких списків у програмі. Списки можуть зустрічатися в програмі тільки як компоненти структури або цілі. Це означає, що безпосередньо в розділі CLAUSES списки зустрічатися не можуть. Визначимо кілька предикатів бази даних, що зберігають наші списки:

DATABASE

цілі(список_цілих)

дійсні(список_дійсних)

тварини(список_тварин)

Тепер у розділ CLAUSES можна помістити конкретні екземпляри цих списків.

CLAUSES

цілі( [1, 5, 7] ).

дійсні( [3.2, 4.6, 5.7, 1.5] ).

тварини( [кішка, собака, кінь] ).

Дістати списки з бази даних ПРОЛОГу можна за допомогою задання цілей (запитів). Наприклад.

GOAL

цілі(Список_Усіх),

дійсні([Перше, Друге, Третє, _ ]),

тварини([ _ , Друга_тварина, _ ]).

Відповідями на цей запит будуть такі конкретизації змінних:

Список_Усіх = [ 1, 5, 7 ]

Перше = 3.2

Друге = 4.6

Третє = 5.7

Друга_тварина = собака

Так

Слід звернути увагу, що якщо якийсь компонент списку непотрібний, необхідно його замінити анонімною змінною. Адже не можна в запиті зіставити список із трьох елементів зі списком з чотирьох.

Для обробки списків необхідно описати програмний предикат у розділі PREDICATES. Наприклад, опишемо предикати друку списків:

PREDICATES

друк_цілих( список_цілих)

друк_вещ(список_вещ)

друк_тварин(список_тварин)

Потім у розділі CLAUSES можна помістити визначення цих предикатів. Тут є одна складність. Вона полягає в тому, що в попередньому прикладі ми отримували списки з бази даних ПРОЛОГу за допомогою запитів, у яких була відома кількість компонентів у кожному списку. Але списки цінні саме тим, що вони можуть містити невизначену кількість компонентів. Нам потрібний спосіб обробки таких списків. Якщо ми не знаємо, яку кількість компонентів містить в список, то їх треба викликати й обробляти по одному.

4.7.3. Метод поділу списку на голову і хвіст (псгх)

Природно обробляти списки, починаючи з голови. Для поділу списку на голову і хвіст можна використати символ “|”. Якщо зіставити список [Head | Tail] з іншим списком, то Head одержить значення елемента, що розташований у голові списку, а Tail – значення хвоста. Слід зазначити, що ціль [Head | Tail] може зіставитися тільки з непорожнім списком. Тому наша процедура друку списку повинна складатися мінімум із двох тверджень. Одне твердження буде визначати наші дії для порожнього списку, а інше – для непорожнього:

CLAUSES

друк_списку( [ ] ).

друк_списку( [Head | Tail] ):-

write(Head), nl,

друк_списку( Tail ).

Завдяки тому, що хвіст списку (Tail) є список, його можна обробити за допомогою цієї ж процедури (з використанням рекурсії). Оскільки операція поділу списку на голову і хвіст може застосовуватися до списків, що містять елементи різного доменного типу, то немає необхідності реалізовувати кілька предикатів для друку списків різних доменних типів. Але описати їх у розділі PREDICATES необхідно з указівкою всіх доменних типів:

PREDICATES

друк_списку(список_цілих)

друк_списку(список_дійсних)

друк_списку(список_тварин)

Такі процедури, аргументами яких можуть бути об'єкти різних типів, у програмуванні прийнято називати поліморфними процедурами. Процедура друк_списку є саме така поліморфна процедура ще й тому, що предикат write у мові TP також є поліморфним.

Поцедура друку списку реалізувалась рекурсивно. І це невипадково. Визначення списку є також рекурсивне. А для рекурсивних структур даних найбільш природний спосіб обробки – рекурсія. Для TP рекурсія до того ж і єдиний можливий спосіб обробки таких структур.