Списки. Полиморфизм
Упражнение 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