Обработка списков
Обработка списков обычно производится рекурсивно. На каждом витке рекурсивного цикла обрабатывается один или несколько смежных элементов. Как правило, на вход такого предиката подаётся один или несколько обрабатываемых списков. На выходе предиката – один или несколько выходных списков.
Пример 4. Возведение в квадрат каждого элемента входного списка:
implement main
open core, console
constants
className = "com/visual-prolog/main".
classVersion = "$JustDate: $$Revision: $".
class predicates
квадрат : (integer* ВходнойСписок) -> integer* СписокКвадратов.
clauses
classInfo(className, classVersion).
квадрат([I|L])=[I*I|квадрат(L)].
квадрат([])=[].
run():-init(),
write(квадрат([10,20,30])),
_=readchar().
end implement main
goal
mainExe::run(main::run).
Пример 5. Расщепить заданный список на два списка. Один должен содержать только нечётные элементы исходного списка, другой – только чётные:
implement main
open core, console
constants
className = "com/visual-prolog/main".
classVersion = "$JustDate: $$Revision: $".
class predicates
чётНечёт : (integer* ВходнойСписок, integer* СписокНечёт, integer* СписокЧёт) procedure (i,o,o).
clauses
classInfo(className, classVersion).
чётНечёт([I|L],[I|Нечёт],Чёт) :- I mod 2 = 1,!, чётНечёт(L,Нечёт,Чёт).
чётНечёт([I|L],Нечёт,[I|Чёт]) :- чётНечёт(L,Нечёт,Чёт).
чётНечёт([],[],[]).
run():-
init(),
чётНечёт([1,2,3,4,5,6,7,8],Нечёт,Чёт),
write(Нечёт),nl,
write(Чёт),nl,
_=readchar().
end implement main
goal
mainExe::run(main::run).
Пример 6. Расщепить заданный список на два списка. Один должен содержать элементы, находящиеся на нечётных позициях исходного списка, другой – на чётных позициях:
implement main
open core, console
constants
className = "com/visual-prolog/main".
classVersion = "$JustDate: $$Revision: $".
class predicates
позиц : (integer* ВходнойСписок, integer* НечётПозиц, integer* ЧётПозиц) procedure (i,o,o).
clauses
classInfo(className, classVersion).
позиц([X,Y|L],[X|Нечёт],[Y|Чёт]) :- !,позиц(L,Нечёт,Чёт).
позиц(L,L,[]).
run():-init(),
позиц([1,3,5,7,9,0,-1,-2],Нечёт,Чёт),
write(Нечёт),nl,
write(Чёт),nl,
_=readchar().
end implement main
goal
mainExe::run(main::run).
Пример 7. Поменять местами элементы, находящиеся на нечётных позициях исходного списка с элементами, находящимися на чётных позициях, то есть первый элемент со вторым, третий – с четвёртым и т.д.:
implement main
open core, console
constants
className = "com/visual-prolog/main".
classVersion = "$JustDate: $$Revision: $".
class predicates
замена : (integer* ВходнойСписок, integer* ВыходнойСписок) procedure (i,o).
clauses
classInfo(className, classVersion).
замена([X,Y|L],[Y,X|W]) :- !,замена(L,W).
замена(L,L).
run():-init(),
замена([1,2,3,4,5,6,7,8,9],L),
write(L),nl,
_=readchar().
end implement main
goal
mainExe::run(main::run).
Пример 8. “Наивный” реверс списка:
implement main
open core, console
constants
className = "com/visual-prolog/main".
classVersion = "$JustDate: $$Revision: $".
class predicates
rev : (integer* ВходнойСписок, integer* РеверсСписок).
clauses
classInfo(className, classVersion).
rev([X|L],R) :- !,rev(L,[X|R]).
rev(_,R) :- write(R).
run():-init(),
rev([1,2,3,4,5,6,7,8,9],[]),
_=readchar().
end implement main
goal
mainExe::run(main::run).
Представленный реверс является “наивным”. Дело в том, что дополняя слева пустой список элементами входного списка, мы получаем реверсированный список только на дне рекурсии, то есть тогда, когда исходный список выбран до последнего элемента. Однако в рассматриваемом предикате rev не предусмотрено вспомогательной переменной, через которую можно “вытащить” результат наверх, в начало рекурсии. Поэтому то нам и пришлось выводить реверсированный список в самой рекурсии, а точнее – в правиле, описывающем условие останова рекурсии.
Добавим в предикат rev ещё один аргумент для того, чтобы передать полученный на дне рекурсии реверсированный список прямо на выход из рекурсии. Третий аргумент при каждом рекурсивном вызове унифицировать сам с собой до тех пор, пока не выполнится условие останова рекурстии.
Пример 9. Реверс списка:
implement main
open core, console
constants
className = "com/visual-prolog/main".
classVersion = "$JustDate: $$Revision: $".
class predicates
reverse : (integer* ВходнойСписок, integer* РеверсСписок, integer* Результат) procedure (i,i,o).
clauses
classInfo(className, classVersion).
reverse([X|L],T,R) :- !,reverse(L,[X|T],R).
reverse(_,R,R).
run():-init(),
reverse([1,2,3,4,5,6,7,8,9],[],R),
write(R),
_=readchar().
end implement main
goal
mainExe::run(main::run).
В вышеописанном примере видно, что в правиле reverse(_,R,R). Второй аргумент, в котором собран реверс исходного списка, унифицируется с третьим аргументом, через который мы и получим результат на выходе из рекурсии.
Пример 10. Полиморфный реверс списка:
implement main
open core, console
constants
className = "com/visual-prolog/main".
classVersion = "$JustDate: $$Revision: $".
class predicates
reverse : (Elem* ВходнойСписок, Elem* РеверсСписок, Elem* Результат) procedure (i,i,o).
clauses
classInfo(className, classVersion).
reverse([X|L],T,R) :- !,reverse(L,[X|T],R).
reverse(_,R,R).
run():-init(),
reverse(["123","abc","абв"],[],R),
write(R),nl,
reverse([8.2,9.4,10.8],[],R1),
write(R1),nl,
_=readchar().
end implement main
goal
mainExe::run(main::run).
Пример 11. Соединение двух списков:
implement main
open core, console
constants className = "com/visual-prolog/main".
classVersion = "$JustDate: $$Revision: $".
class predicates
append : (Elem* Список1, Elem* Список2, Elem* Результат) procedure (i,i,o).
clauses
classInfo(className, classVersion).
append([X|L1],L2,[X|L]) :- !,append(L1,L2,L).
append(_,L,L).
run():-init(),
append(["123","abc","абв"],["prolog","пролог"],L),
write(L),nl,
append([8.2,9.4,10.8],[-0.999,123.45],L1),
write(L1),nl,
_=readchar().
end implement main
goal
mainExe::run(main::run).
Задача 8. Написать предикат получения списка, составленного из квадратных корней каждого элемента входного списка.
Задача 9. Написать предикат member(Elem,Elem*) истинный тогда, когда элемент Elem принадлежит списку Elem*.
Задача 10. Написать предикат last(Elem,Elem*), возвращающий последний элемент Elem списка Elem*.
Задача 11. Написать предикат nth(Elem,N,Elem*), возвращающий значение N-го элемента Elem списка Elem*.
Задача 12. Написать предикат set(Elem,N,L,L1), возвращающий новый список L1, который образован из входного списка L путём замены в нём значения N-го элемента новым значением Elem.
Задача 13. Написать предикат max(Elem*,Max), который находит максимальный элемент заданного списка.
Задача 14. Разработать функцию, возвращающую сумму всех элементов списка.
Задача 15. Разработать функцию, вычисляющую среднее арифметическое элементов заданного списка чисел.
Задача 16. Разработать функцию, выделяющую из исходного списка подсписок, начиная с элемента с номером N и заканчивая элементом с номером N+K. N и K – аргументы функции.
Задача 17. Разработать функцию, возвращающую 0, если в исходном списке из нулей и единиц, больше нулей, и 1 – в противном случае.
Задача 18. Разработать полиморфный предикат слияние(L1,L2,L3), который производит операцию объединения двух упорядоченных по убыванию списков L1, L2 в третий, упорядоченный по убыванию список L3.
Задача повышенной трудности 1. Написать предикат сортировки sort(L,L1), который сортирует входной список L любым методом сортировки в выходной список L1.
Задача повышенной трудности 2. Дан список координат точек на плоскости, например: [[1,3],[2,5],[3,-8],[1,1],[1,0],[2,-2]]. Необходимо вывести координаты самой ближней к центру координат точки, и самой дальней.