This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/library/lists.yap
Vítor Santos Costa 3164ed2d61 doc support
2015-01-04 23:58:23 +00:00

690 lines
17 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.
%
/** @defgroup Lists List Manipulation
@ingroup library
@{
The following list manipulation routines are available once included
with the `use_module(library(lists))` command.
*/
/**
@pred append(? _Prefix_,? _Suffix_,? _Combined_)
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_, _Suffix_ or to take
a given _Combined_ apart.
*/
/** @pred append(? _Lists_,? _Combined_)
Holds if the lists of _Lists_ can be concatenated as a
_Combined_ list.
*/
/** @pred flatten(+ _List_, ? _FlattenedList_)
Flatten a list of lists _List_ into a single list
_FlattenedList_.
~~~~~{.prolog}
?- flatten([[1],[2,3],[4,[5,6],7,8]],L).
L = [1,2,3,4,5,6,7,8] ? ;
no
~~~~~
*/
/** @pred intersection(+ _Set1_, + _Set2_, + _Set3_)
Succeeds if _Set3_ unifies with the intersection of _Set1_ and
_Set2_. _Set1_ and _Set2_ are lists without duplicates. They
need not be ordered.
*/
/** @pred last(+ _List_,? _Last_)
True when _List_ is a list and _Last_ is identical to its last element.
*/
/** @pred list_concat(+ _Lists_,? _List_)
True when _Lists_ is a list of lists and _List_ is the
concatenation of _Lists_.
*/
/** @pred max_list(? _Numbers_, ? _Max_)
True when _Numbers_ is a list of numbers, and _Max_ is the maximum.
*/
/** @pred min_list(? _Numbers_, ? _Min_)
True when _Numbers_ is a list of numbers, and _Min_ is the minimum.
*/
/** @pred nth(? _N_, ? _List_, ? _Elem_)
The same as nth1/3.
*/
/** @pred nth(? _N_, ? _List_, ? _Elem_, ? _Rest_)
Same as `nth1/4`.
*/
/** @pred nth0(? _N_, ? _List_, ? _Elem_)
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 member/2
*/
/** @pred 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/4` is the same except that it counts from 1. `nth0/4`
can be used to insert _Elem_ after the Nth element of _Rest_.
*/
/** @pred nth1(+ _Index_,? _List_,? _Elem_)
Succeeds when the _Index_-th element of _List_ unifies with
_Elem_. Counting starts at 1.
Set environment variable. _Name_ and _Value_ should be
instantiated to atoms or integers. The environment variable will be
passed to `shell/[0-2]` and can be requested using `getenv/2`.
They also influence expand_file_name/2.
*/
/** @pred nth1(? _N_, ? _List_, ? _Elem_)
The same as nth0/3, except that it counts from
1, that is `nth(1, [H|_], H)`.
*/
/** @pred nth1(? _N_, ? _List_, ? _Elem_, ? _Rest_)
Unifies _Elem_ with the Nth element of _List_, counting from 1,
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. `nth(3, List, c, [a,b,d,e])` unifies List with `[a,b,c,d,e]`. `nth/4`
can be used to insert _Elem_ after the Nth element of _Rest_.
*/
/** @pred numlist(+ _Low_, + _High_, + _List_)
If _Low_ and _High_ are integers with _Low_ =<
_High_, unify _List_ to a list `[Low, Low+1, ...High]`. See
also between/3.
*/
/** @pred permutation(+ _List_,? _Perm_)
True when _List_ and _Perm_ are permutations of each other.
*/
/** @pred remove_duplicates(+ _List_, ? _Pruned_)
Removes duplicated elements from _List_. Beware: if the _List_ has
non-ground elements, the result may surprise you.
*/
/** @pred same_length(? _List1_, ? _List2_)
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, ...
*/
/** @pred select(? _Element_, ? _List_, ? _Residue_)
True when _Set_ is a list, _Element_ occurs in _List_, and
_Residue_ is everything in _List_ except _Element_ (things
stay in the same order).
*/
/** @pred selectchk(? _Element_, ? _List_, ? _Residue_)
Semi-deterministic selection from a list. Steadfast: defines as
~~~~~{.prolog}
selectchk(Elem, List, Residue) :-
select(Elem, List, Rest0), !,
Rest = Rest0.
~~~~~
*/
/** @pred sublist(? _Sublist_, ? _List_)
True when both `append(_,Sublist,S)` and `append(S,_,List)` hold.
*/
/** @pred subtract(+ _Set_, + _Delete_, ? _Result_)
Delete all elements from _Set_ that occur in _Delete_ (a set)
and unify the result with _Result_. Deletion is based on
unification using memberchk/2. The complexity is
`|Delete|\*|Set|`.
See ord_subtract/3.
*/
/** @pred suffix(? _Suffix_, ? _List_)
Holds when `append(_,Suffix,List)` holds.
*/
/** @pred sum_list(? _Numbers_, + _SoFar_, ? _Total_)
True when _Numbers_ is a list of numbers, and _Total_ is the sum of their total plus _SoFar_.
*/
/** @pred sum_list(? _Numbers_, ? _Total_)
True when _Numbers_ is a list of numbers, and _Total_ is their sum.
*/
/** @pred sumlist(? _Numbers_, ? _Total_)
True when _Numbers_ is a list of integers, and _Total_ is their
sum. The same as sum_list/2, please do use sum_list/2
instead.
*/
:- module(lists,
[
append/3,
append/2,
delete/3,
intersection/3,
flatten/2,
last/2,
list_concat/2,
max_list/2,
list_to_set/2,
member/2,
memberchk/2,
min_list/2,
nextto/3,
nth/3,
nth/4,
nth0/3,
nth0/4,
nth1/3,
nth1/4,
numlist/3,
permutation/2,
prefix/2,
remove_duplicates/2,
reverse/2,
same_length/2,
select/3,
selectchk/3,
sublist/2,
substitute/4,
subtract/3,
suffix/2,
sum_list/2,
sum_list/3,
sumlist/2
]).
:- use_module(library(error),
[must_be/2]).
%% 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], L).
append_([L1,L2], L) :-
append(L1,L2,L).
append_([L1,L2|[L3|LL]], L) :-
append(L1,L2,LI),
append_([LI|[L3|LL]],L).
% 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, [E|List], El, [E|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(?Sub, +List) is nondet.
%
% True if all elements of Sub appear in List in the same order.
sublist(L, L).
sublist(Sub, [H|T]) :-
'$sublist1'(T, H, Sub).
'$sublist1'(Sub, _, Sub).
'$sublist1'([H|T], _, Sub) :-
'$sublist1'(T, H, Sub).
'$sublist1'([H|T], X, [X|Sub]) :-
'$sublist1'(T, H, Sub).
% 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).
% copied from SWI lists library.
intersection([], _, []) :- !.
intersection([X|T], L, Intersect) :-
memberchk(X, L), !,
Intersect = [X|R],
intersection(T, L, R).
intersection([_|T], L, R) :-
intersection(T, L, R).
%% subtract(+Set, +Delete, -Result) is det.
%
% Delete all elements from `Set' that occur in `Delete' (a set)
% and unify the result with `Result'. Deletion is based on
% unification using memberchk/2. The complexity is |Delete|*|Set|.
%
% @see ord_subtract/3.
subtract([], _, []) :- !.
subtract([E|T], D, R) :-
memberchk(E, D), !,
subtract(T, D, R).
subtract([H|T], D, [H|R]) :-
subtract(T, D, R).
%% list_to_set(+List, ?Set) is det.
%
% True when Set has the same element as List in the same order.
% The left-most copy of the duplicate is retained. The complexity
% of this operation is |List|^2.
%
% @see sort/2.
list_to_set(List, Set) :-
list_to_set_(List, Set0),
Set = Set0.
list_to_set_([], R) :-
close_list(R).
list_to_set_([H|T], R) :-
memberchk(H, R), !,
list_to_set_(T, R).
close_list([]) :- !.
close_list([_|T]) :-
close_list(T).