Опубликован: 12.02.2014 | Доступ: свободный | Студентов: 922 / 239 | Длительность: 11:22:00
Специальности: Программист
Лекция 6:

Списки. Полиморфизм

< Лекция 5 || Лекция 6: 123 || Лекция 7 >

Упражнение 5. Определите предикат, который:

  1. заменяет каждый второй элемент списка заданным элементом, не используя счетчик;
  2. удаляет каждый n-й элемент списка.

В следующей программе определена операция обращения списка, в результате выполнения которой список записывается в обратном порядке. Например, список [1, 2, 3] преобразуется в список [3, 2, 1]. В определении операции используется вспомогательный аргумент — список, в который перекладываются по одному элементы исходного списка.

class predicates
    reverse: (A*) -> A*.
    reverse: (A*, A*) -> A*.
clauses
    reverse(L) = reverse(L, []).

    reverse([], L) = L.
    reverse([A | L], L1) = reverse(L, [A | L1]).

    run():-
        write(reverse([1, 2, 3, 4, 5])), nl,
        write(reverse([1, 2], [3, 4, 5])),
    _ = readLine().
Пример 6.6. Обращение списка

В классе list имеется предикат reverse/1, который выполняет операцию обращения списка.

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

class predicates
    split: (unsigned*, unsigned* [out], unsigned* [out]).
clauses
    split([A | L], [A | L1], L2):-
        A mod 2 = 0,
        !,
        split(L, L1, L2).
    split([A | L], L1, [A | L2]):-
        split(L, L1, L2).
    split([], [], []).

    run():-
        split([1, 2, 3, 4, 5, 9, 8, 7, 6], L1, L2),
        write(L1), nl,
        write(L2),
        _ = readLine().
Пример 6.7. Разделение на списки четных и нечетных элементов

Следующая программа посвящена двум важным предикатам. Первый из них — это предикат delete, который удаляет первое вхождение элемента в список. Если такого элемента в списке нет, предикат принимает значение ложь. Второй предикат — это предикат select. Предикат можно использовать двояким образом. С одной стороны, он недетерминированно возвращает произвольный элемент списка и список без этого элемента. С другой стороны, он недетерминированно вставляет заданный элемент на произвольное место в заданном списке, так что в результате получается новый список.

class predicates
    delete: (A, A*) -> A* determ.
    select: (A, A*, A*) nondeterm (o,i,o) multi (i,o,i).
clauses
    delete(A, [A | L]) = L:- !.
    delete(A, [B | L]) = [B | delete(A, L)].

    select(A, [A | L], L).
    select(A, [B | L], [B | L1]):-
        select(A, L, L1).

    run():-
        L = delete(4, [1, 2, 3, 4, 5]),
        write(L), nl,
        fail;
        nl,
        select(A, [1, 2, 3, 4, 5], L),
            write(A, " - ", L), nl,
        fail;
        nl,
        select(100, L, [1, 2, 3, 4, 5]),
            write(L), nl,
        fail;
        _ = readLine().
Пример 6.8. Предикаты delete и select

Как упоминалось выше, элементы списка в языке Visual Prolog должны принадлежать одному и тому же домену. Поэтому в нем нельзя использовать вложенные списки напрямую и строить термы вида [[0], 1, [2, 3, [4, 5, []]]]. Но такие списки можно смоделировать. В следующей программе рекурсивно определяется домен элементов списка, включающий как атомарные элементы, так и вложенные списки. Аргументом функтора list/1 является список элементов исходного домена. Атомы — это термы с функтором at/1. В программе реализуется операция линеаризации списка — приведения его к списку атомарных элементов. Идея определения операции линеаризации с помощью вспомогательного списка, играющего роль стека, описана в [9].

domains
    elem{A} = at(A); list(elem{A}*).

class predicates
    flatten: (elem{A}*) -> elem{A}*.
    flatten: (elem{A}*, elem{A}**) -> elem{A}*.
clauses
    flatten(List) = flatten(List, []).

    flatten([list(L) | Tail], AuxList) =  flatten(L, [Tail | AuxList]):- !.
    flatten([Head | Tail], AuxList) =  [Head | flatten(Tail, AuxList)].
    flatten([], [L | AuxList]) =  flatten(L, AuxList).
    flatten([], []) = [].

    run():-
        L = [list([at(0)]), at(1), 
                list([at(2), at(3), list([at(4), at(5), list([])])])],
        writef("%\n%", L, flatten(L)),
        _ = readLine().
Пример 6.9. Линеаризация списка

В языке Visual Prolog имеются развитые средства обработки исключений. Например, если функция определена только на непустых списках, то ее можно "доопределить" так, что она станет всюду определенной:

class predicates
    first: (Elem*) -> Elem*.
clauses
    first([H | _]) = [H].
    first([]) = _:-
        exception::raise_error(predicate_name(),
            "  Input list is empty.").

Если аргумент предиката first равен пустому списку, то возбуждается исключение. Предикат predicate_name возвращает имя предиката, в определении которого он участвует.

Упражнения

  1. Проверьте, является ли список палиндромом.
  2. Вычислите среднее арифметическое элементов списка, состоящего из целых чисел.
  3. Проверьте, каждый ли элемент первого списка, содержится во втором списке.
  4. Проверьте, является ли список упорядоченным по возрастанию или по убыванию.
  5. Найдите позицию первого вхождения подсписка в список.
  6. Вычислите все позиции заданного элемента в списке.
  7. Разбейте список на отрезки длиной по n элементов. Последний подсписок может содержать меньшее число элементов.
  8. Поменяйте местами два элемента списка, стоящих на заданных позициях.
  9. Вычислите номер заданного атомарного элемента в списке, содержащем вложенные списки (см. листинг 6.9).
< Лекция 5 || Лекция 6: 123 || Лекция 7 >
Жаныл Айкын
Жаныл Айкын
Rustam Inatov
Rustam Inatov

Доброго времени суток, подскажите пожалуйста, visual prolog examples, pie, vip7.5 - это все, где я могу скачать? (в смысле) может быть на сайте есть какой-то архив? Увы я не нашел его.

Подскажите, пожалуйста.

С уважением, Рустам.