% 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).