393 lines
10 KiB
Prolog
393 lines
10 KiB
Prolog
% This file has been included as an YAP library by Vitor Santos Costa, 1999
|
|
|
|
%
|
|
% This file includes code from Bob Welham, Lawrence Byrd, and R. A. O'Keefe.
|
|
%
|
|
:- module(lists,
|
|
[
|
|
append/3,
|
|
append/2,
|
|
delete/3,
|
|
last/2,
|
|
member/2,
|
|
memberchk/2,
|
|
nextto/3,
|
|
nth/3,
|
|
nth/4,
|
|
nth0/3,
|
|
nth0/4,
|
|
nth1/3,
|
|
nth1/4,
|
|
permutation/2,
|
|
prefix/2,
|
|
remove_duplicates/2,
|
|
reverse/2,
|
|
same_length/2,
|
|
select/3,
|
|
selectchk/3,
|
|
sublist/2,
|
|
substitute/4,
|
|
sum_list/2,
|
|
sum_list/3,
|
|
suffix/2,
|
|
sumlist/2,
|
|
list_concat/2,
|
|
flatten/2,
|
|
max_list/2,
|
|
min_list/2,
|
|
numlist/3
|
|
]).
|
|
|
|
:- ensure_loaded(library(error)).
|
|
|
|
|
|
% append(Prefix, Suffix, Combined)
|
|
% is true when all three arguments are lists, and the members of Combined
|
|
% are the members of Prefix followed by the members of Suffix. It may be
|
|
% used to form Combined from a given Prefix and Suffix, or to take a given
|
|
% Combined apart. E.g. we could define member/2 (from SetUtl.Pl) as
|
|
% member(X, L) :- append(_, [X|_], L).
|
|
|
|
append([], L, L).
|
|
append([H|T], L, [H|R]) :-
|
|
append(T, L, R).
|
|
|
|
|
|
%% append(+ListOfLists, ?List)
|
|
%
|
|
% Concatenate a list of lists. Is true if Lists is a list of
|
|
% lists, and List is the concatenation of these lists.
|
|
%
|
|
% @param ListOfLists must be a list of -possibly- partial lists
|
|
|
|
append(ListOfLists, List) :-
|
|
% must_be(list, ListOfLists),
|
|
append_(ListOfLists, List).
|
|
|
|
append_([], []).
|
|
append_([L|Ls], As) :-
|
|
append(L, Ws, As),
|
|
append_(Ls, Ws).
|
|
|
|
|
|
% delete(List, Elem, Residue)
|
|
% is true when List is a list, in which Elem may or may not occur, and
|
|
% Residue is a copy of List with all elements identical to Elem deleted.
|
|
|
|
delete([], _, []).
|
|
delete([Head|List], Elem, Residue) :-
|
|
Head == Elem, !,
|
|
delete(List, Elem, Residue).
|
|
delete([Head|List], Elem, [Head|Residue]) :-
|
|
delete(List, Elem, Residue).
|
|
|
|
|
|
% last(List, Last)
|
|
% is true when List is a List and Last is identical to its last element.
|
|
% This could be defined as last(L, X) :- append(_, [X], L).
|
|
|
|
last([H|List], Last) :-
|
|
last(List, H, Last).
|
|
|
|
last([], Last, Last).
|
|
last([H|List], _, Last) :-
|
|
last(List, H, Last).
|
|
|
|
% nextto(X, Y, List)
|
|
% is true when X and Y appear side-by-side in List. It could be written as
|
|
% nextto(X, Y, List) :- append(_, [X,Y,_], List).
|
|
% It may be used to enumerate successive pairs from the list.
|
|
|
|
nextto(X,Y, [X,Y|_]).
|
|
nextto(X,Y, [_|List]) :-
|
|
nextto(X,Y, List).
|
|
|
|
% nth0(?N, +List, ?Elem) is true when Elem is the Nth member of List,
|
|
% counting the first as element 0. (That is, throw away the first
|
|
% N elements and unify Elem with the next.) It can only be used to
|
|
% select a particular element given the list and index. For that
|
|
% task it is more efficient than nmember.
|
|
% nth(+N, +List, ?Elem) is the same as nth0, except that it counts from
|
|
% 1, that is nth(1, [H|_], H).
|
|
|
|
nth0(V, In, Element) :- var(V), !,
|
|
generate_nth(0, V, In, Element).
|
|
nth0(0, [Head|_], Head) :- !.
|
|
nth0(N, [_|Tail], Elem) :-
|
|
M is N-1,
|
|
find_nth0(M, Tail, Elem).
|
|
|
|
find_nth0(0, [Head|_], Head) :- !.
|
|
find_nth0(N, [_|Tail], Elem) :-
|
|
M is N-1,
|
|
find_nth0(M, Tail, Elem).
|
|
|
|
|
|
nth1(V, In, Element) :- var(V), !,
|
|
generate_nth(1, V, In, Element).
|
|
nth1(1, [Head|_], Head) :- !.
|
|
nth1(N, [_|Tail], Elem) :-
|
|
nonvar(N), !,
|
|
M is N-1, % should be succ(M, N)
|
|
find_nth(M, Tail, Elem).
|
|
|
|
nth(V, In, Element) :- var(V), !,
|
|
generate_nth(1, V, In, Element).
|
|
nth(1, [Head|_], Head) :- !.
|
|
nth(N, [_|Tail], Elem) :-
|
|
nonvar(N), !,
|
|
M is N-1, % should be succ(M, N)
|
|
find_nth(M, Tail, Elem).
|
|
|
|
find_nth(1, [Head|_], Head) :- !.
|
|
find_nth(N, [_|Tail], Elem) :-
|
|
M is N-1,
|
|
find_nth(M, Tail, Elem).
|
|
|
|
|
|
generate_nth(I, I, [Head|_], Head).
|
|
generate_nth(I, IN, [_|List], El) :-
|
|
I1 is I+1,
|
|
generate_nth(I1, IN, List, El).
|
|
|
|
|
|
|
|
% nth0(+N, ?List, ?Elem, ?Rest) unifies Elem with the Nth element of List,
|
|
% counting from 0, and Rest with the other elements. It can be used
|
|
% to select the Nth element of List (yielding Elem and Rest), or to
|
|
% insert Elem before the Nth (counting from 1) element of Rest, when
|
|
% it yields List, e.g. nth0(2, List, c, [a,b,d,e]) unifies List with
|
|
% [a,b,c,d,e]. nth is the same except that it counts from 1. nth
|
|
% can be used to insert Elem after the Nth element of Rest.
|
|
|
|
nth0(V, In, Element, Tail) :- var(V), !,
|
|
generate_nth(0, V, In, Element, Tail).
|
|
nth0(0, [Head|Tail], Head, Tail) :- !.
|
|
nth0(N, [Head|Tail], Elem, [Head|Rest]) :-
|
|
M is N-1,
|
|
nth0(M, Tail, Elem, Rest).
|
|
|
|
find_nth0(0, [Head|Tail], Head, Tail) :- !.
|
|
find_nth0(N, [Head|Tail], Elem, [Head|Rest]) :-
|
|
M is N-1,
|
|
find_nth0(M, Tail, Elem, Rest).
|
|
|
|
|
|
|
|
nth1(V, In, Element, Tail) :- var(V), !,
|
|
generate_nth(1, V, In, Element, Tail).
|
|
nth1(1, [Head|Tail], Head, Tail) :- !.
|
|
nth1(N, [Head|Tail], Elem, [Head|Rest]) :-
|
|
M is N-1,
|
|
nth1(M, Tail, Elem, Rest).
|
|
|
|
nth(V, In, Element, Tail) :- var(V), !,
|
|
generate_nth(1, V, In, Element, Tail).
|
|
nth(1, [Head|Tail], Head, Tail) :- !.
|
|
nth(N, [Head|Tail], Elem, [Head|Rest]) :-
|
|
M is N-1,
|
|
nth(M, Tail, Elem, Rest).
|
|
|
|
find_nth(1, [Head|Tail], Head, Tail) :- !.
|
|
find_nth(N, [Head|Tail], Elem, [Head|Rest]) :-
|
|
M is N-1,
|
|
find_nth(M, Tail, Elem, Rest).
|
|
|
|
|
|
generate_nth(I, I, [Head|Tail], Head, Tail).
|
|
generate_nth(I, IN, [_|List], El, Tail) :-
|
|
I1 is I+1,
|
|
generate_nth(I1, IN, List, El, Tail).
|
|
|
|
|
|
|
|
% permutation(List, Perm)
|
|
% is true when List and Perm are permutations of each other. Of course,
|
|
% if you just want to test that, the best way is to keysort/2 the two
|
|
% lists and see if the results are the same. Or you could use list_to_bag
|
|
% (from BagUtl.Pl) to see if they convert to the same bag. The point of
|
|
% perm is to generate permutations. The arguments may be either way round,
|
|
% the only effect will be the order in which the permutations are tried.
|
|
% Be careful: this is quite efficient, but the number of permutations of an
|
|
% N-element list is N!, even for a 7-element list that is 5040.
|
|
|
|
permutation([], []).
|
|
permutation(List, [First|Perm]) :-
|
|
select(First, List, Rest), % tries each List element in turn
|
|
permutation(Rest, Perm).
|
|
|
|
|
|
% prefix(Part, Whole) iff Part is a leading substring of Whole
|
|
|
|
prefix([], _).
|
|
prefix([Elem | Rest_of_part], [Elem | Rest_of_whole]) :-
|
|
prefix(Rest_of_part, Rest_of_whole).
|
|
|
|
% remove_duplicates(List, Pruned)
|
|
% removes duplicated elements from List. Beware: if the List has
|
|
% non-ground elements, the result may surprise you.
|
|
|
|
remove_duplicates([], []).
|
|
remove_duplicates([Elem|L], [Elem|NL]) :-
|
|
delete(L, Elem, Temp),
|
|
remove_duplicates(Temp, NL).
|
|
|
|
% reverse(List, Reversed)
|
|
% is true when List and Reversed are lists with the same elements
|
|
% but in opposite orders. rev/2 is a synonym for reverse/2.
|
|
|
|
reverse(List, Reversed) :-
|
|
reverse(List, [], Reversed).
|
|
|
|
reverse([], Reversed, Reversed).
|
|
reverse([Head|Tail], Sofar, Reversed) :-
|
|
reverse(Tail, [Head|Sofar], Reversed).
|
|
|
|
|
|
% same_length(?List1, ?List2)
|
|
% is true when List1 and List2 are both lists and have the same number
|
|
% of elements. No relation between the values of their elements is
|
|
% implied.
|
|
% Modes same_length(-,+) and same_length(+,-) generate either list given
|
|
% the other; mode same_length(-,-) generates two lists of the same length,
|
|
% in which case the arguments will be bound to lists of length 0, 1, 2, ...
|
|
|
|
same_length([], []).
|
|
same_length([_|List1], [_|List2]) :-
|
|
same_length(List1, List2).
|
|
|
|
%% selectchk(+Elem, +List, -Rest) is semidet.
|
|
%
|
|
% Semi-deterministic removal of first element in List that unifies
|
|
% Elem.
|
|
|
|
selectchk(Elem, List, Rest) :-
|
|
select(Elem, List, Rest0), !,
|
|
Rest = Rest0.
|
|
|
|
|
|
% select(?Element, ?Set, ?Residue)
|
|
% is true when Set is a list, Element occurs in Set, and Residue is
|
|
% everything in Set except Element (things stay in the same order).
|
|
|
|
select(Element, [Element|Rest], Rest).
|
|
select(Element, [Head|Tail], [Head|Rest]) :-
|
|
select(Element, Tail, Rest).
|
|
|
|
|
|
% sublist(Sublist, List)
|
|
% is true when both append(_,Sublist,S) and append(S,_,List) hold.
|
|
|
|
sublist(Sublist, List) :-
|
|
prefix(Sublist, List).
|
|
sublist(Sublist, [_|List]) :-
|
|
sublist(Sublist, List).
|
|
|
|
% substitute(X, XList, Y, YList)
|
|
% is true when XList and YList only differ in that the elements X in XList
|
|
% are replaced by elements Y in the YList.
|
|
substitute(X, XList, Y, YList) :-
|
|
substitute2(XList, X, Y, YList).
|
|
|
|
substitute2([], _, _, []).
|
|
substitute2([X0|XList], X, Y, [Y|YList]) :-
|
|
X == X0, !,
|
|
substitute2(XList, X, Y, YList).
|
|
substitute2([X0|XList], X, Y, [X0|YList]) :-
|
|
substitute2(XList, X, Y, YList).
|
|
|
|
% suffix(Suffix, List)
|
|
% holds when append(_,Suffix,List) holds.
|
|
suffix(Suffix, Suffix).
|
|
suffix(Suffix, [_|List]) :-
|
|
suffix(Suffix,List).
|
|
|
|
% sumlist(Numbers, Total)
|
|
% is true when Numbers is a list of integers, and Total is their sum.
|
|
|
|
sumlist(Numbers, Total) :-
|
|
sumlist(Numbers, 0, Total).
|
|
|
|
sum_list(Numbers, SoFar, Total) :-
|
|
sumlist(Numbers, SoFar, Total).
|
|
|
|
sum_list(Numbers, Total) :-
|
|
sumlist(Numbers, 0, Total).
|
|
|
|
sumlist([], Total, Total).
|
|
sumlist([Head|Tail], Sofar, Total) :-
|
|
Next is Sofar+Head,
|
|
sumlist(Tail, Next, Total).
|
|
|
|
|
|
% list_concat(Lists, List)
|
|
% is true when Lists is a list of lists, and List is the
|
|
% concatenation of these lists.
|
|
|
|
list_concat([], []).
|
|
list_concat([H|T], L) :-
|
|
list_concat(H, L, Li),
|
|
list_concat(T, Li).
|
|
|
|
list_concat([], L, L).
|
|
list_concat([H|T], [H|Lf], Li) :-
|
|
list_concat(T, Lf, Li).
|
|
|
|
|
|
|
|
%
|
|
% flatten a list
|
|
%
|
|
flatten(X,Y) :- flatten_list(X,Y,[]).
|
|
|
|
flatten_list(V) --> {var(V)}, !, [V].
|
|
flatten_list([]) --> !.
|
|
flatten_list([H|T]) --> !, flatten_list(H),flatten_list(T).
|
|
flatten_list(H) --> [H].
|
|
|
|
max_list([H|L],Max) :-
|
|
max_list(L,H,Max).
|
|
|
|
max_list([],Max,Max).
|
|
max_list([H|L],Max0,Max) :-
|
|
(
|
|
H > Max0
|
|
->
|
|
max_list(L,H,Max)
|
|
;
|
|
max_list(L,Max0,Max)
|
|
).
|
|
|
|
min_list([H|L],Max) :-
|
|
min_list(L,H,Max).
|
|
|
|
min_list([],Max,Max).
|
|
min_list([H|L],Max0,Max) :-
|
|
(
|
|
H < Max0
|
|
->
|
|
min_list(L, H, Max)
|
|
;
|
|
min_list(L, Max0, Max)
|
|
).
|
|
|
|
%% numlist(+Low, +High, -List) is semidet.
|
|
%
|
|
% List is a list [Low, Low+1, ... High]. Fails if High < Low.%
|
|
%
|
|
% @error type_error(integer, Low)
|
|
% @error type_error(integer, High)
|
|
|
|
numlist(L, U, Ns) :-
|
|
must_be(integer, L),
|
|
must_be(integer, U),
|
|
L =< U,
|
|
numlist_(L, U, Ns).
|
|
|
|
numlist_(U, U, OUT) :- !, OUT = [U].
|
|
numlist_(L, U, [L|Ns]) :-
|
|
succ(L, L2),
|
|
numlist_(L2, U, Ns).
|
|
|
|
|