Списки. Полиморфизм
Упражнение 2. Определите предикаты, которые возвращают:
- каждый второй элемент списка;
- только второй элемент списка;
- элементы списка целых чисел, делящиеся на 3 без остатка.
В классе list, который входит в набор Prolog Foundation Classes (PFC) основных классов языка Visual Prolog имеется предикат проверки принадлежности элемента списку isMember/2 и предикат getMember_nd/1, недетерминированно возвращающий элементы списка и определенный в виде функции. В версии 7.5 языка Visual Prolog появился оператор in, который можно использовать для выполнения обеих данных операций. Например:
if 2 in [1, 2, 3] then write("2 принадлежит списку") else write("2 не принадлежит списку") end if, nl, nl, foreach X in [1, 2, 3] do write(X), nl end foreach.
Следующая программа посвящена предикату append, определение которого позволяет использовать его большим количеством способов, в частности:
- для соединения списков;
- для определения префикса (начального отрезка списка), который нужно присоединить к заданному суффиксу (конечному отрезку списка), чтобы получить заданный список;
- для определения суффикса, который нужно присоединить к заданному префиксу, чтобы получить заданный список;
- для поиска всех возможных разбиений на префиксы и суффиксы;
- для многого другого.
class predicates append: (A*, A*, A*) nondeterm anyflow. append: (A*, A*) -> A*. clauses append([], L, L). % разнообразное использование append([H | L1], L2, [H | L]):- append(L1, L2, L). append([], L) = L. % соединение двух списков в один append([H | L1], L2) = [H | append(L1, L2)]. run():- append([1, 2], [2, 3], L0), write(L0), nl, write(append([1, 2], [2, 3])), nl, L = [1, 2, 3, 4, 5], append(L1, [4, 5], L), write(L1), nl, append([1, 2, 3], L2, L), write(L2), nl, nl, append(P, S, L), writef("% - %\n", P, S), fail; L = [1, 2, 3, 4, 5, 6, 7], append(_, [X, _, _], L), writef("\nТретий с конца элемент списка - %", X), append([_, _, Y], _, L), writef("\nТретий с начала элемент списка - %", Y), fail; _ = readLine().Пример 6.3. Соединение списков
Упражнение 3. Найдите с помощью предиката append:
- последний элемент списка;
- префиксы списка;
- суффиксы списка;
- список без двух последних элементов исходного списка.
В классе list определены предикаты append, которые могут иметь от двух до пяти аргументов, выполняющие операцию соединения списков. Кроме этого, имеется предикат appendList/1, который соединяет список списков элементов в один список.
В приведенной ниже программе непосредственно генерируются подсписки списка — префиксы, суффиксы и все подсписки.
class predicates prefix: (A*) -> A* multi. suffix: (A*) -> A* multi. sublist: (A*) -> A* nondeterm. clauses prefix(_) = []. % префикс списка prefix([H | T]) = [H | prefix(T)]. suffix(L) = L. % суффикс списка suffix([_| T]) = suffix(T). sublist([]) = []. % подсписок списка sublist(L) = S:- S = prefix(L), S <> []. sublist([_ | L]) = sublist(L). run():- write(prefix([1, 2, 3, 4])), nl, fail; nl, write(suffix([1, 2, 3, 4])), nl, fail; nl, write(sublist([1, 2, 3, 4])), nl, fail; _ = readLine().Пример 6.4. Подсписки списка
Упражнение 4.
- Сгенерируйте подсписки списка четной длины, не используя предикат вычисления длины списка.
- Определите детерминированный предикат, который по двум входным спискам определяет, является ли первый из них подсписком второго списка.
В приведенной ниже программе реализованы операция замены одного элемента списка другим, а также операция замены каждого n-го элемента списка заданным элементом. В определении второго предиката используется счетчик.
class predicates replace: (A*, A What, A With) -> A*. replace_nth: (A*, positive N, A With) -> A*. replace_nth: (A*, positive Counter, positive N, A With) -> A*. clauses replace([A | T], A, B) = [B | replace(T, A, B)]:- !. replace([H | T], A, B) = [H | replace(T, A, B)]. replace([], _, _) = []. replace_nth(L, N, A) = replace_nth(L, N, N, A):- N > 1, !. replace_nth(L, _, _) = L. replace_nth([_ | L], 1, N, A) = [A | replace_nth(L, N, N, A)]:- !. replace_nth([H | L], C, N, A) = [H | replace_nth(L, C - 1, N, A)]. replace_nth([], _, _, _) = []. run():- L = [math::random(10) || _ = std::fromTo(1, 20)], write(L), nl, write(replace(L, 0, 100)), nl, write(replace_nth(L, 6, 333)), nl, _ = readLine().Пример 6.5. Замена элементов списка
Предикат fromTo (см. определение предиката run) недетерминированно возвращает целые числа в заданных пределах. Например, цель
X = fromTo(1, 3)
имеет следующие решения:
X = 1 X = 2 X = 3