Prolog library: bagutl.pl
pereira at sri-unix.UUCP
pereira at sri-unix.UUCP
Mon Aug 22 15:50:55 AEST 1983
/* BAGUTL.PL Bag Utilities
R.A.O'Keefe. Updated: 10 Sept 81
A bag B is a function from a set dom(B) to the non-negative integers.
For the purposes of this module, a bag is constructed from two functions:
bag - creates an empty bag
bag(E, M, B) - extends the bag B with a new (NB!) element E
which occurs with multiplicity M, and which
precedes all elements of B in Prolog's order.
A bag is represented by a Prolog term mirroring its construction. There
is one snag with this: what are we to make of
bag(f(a,Y), 1, bag(f(X,b), 1, bag)) ?
As a term it has two distinct elements, but f(a,b) will be reported as
occurring in it twice. But according to the definition above,
bag(f(a,b), 1, bag(f(a,b), 1, bag))
is not the representation of any bag, that bag is represented by
bag(f(a,b), 2, bag)
alone. We are apparently stuck with a scheme which is only guaranteed
to work for ground terms.
The reason for insisting on the order is to make union and
intersection linear in the sizes of their arguments.
*/
:- public is_bag/1.
:- mode is_bag(+), is_bag(+, +), posint(+).
is_bag(bag(E, M, B)) :-
posint(M),
is_bag(B, E).
is_bag(bag).
is_bag(bag(E, M, B), P) :-
E @> P,
posint(M),
is_bag(B, E).
is_bag(bag, P).
posint(M) :-
integer(M),
M > 0.
:- public portray_bag/1.
:- mode portray_bag(+), pbag(+, +, +), pbag(+, +).
portray_bag(bag(E,M,B)) :-
write('[% '), pbag(E, M, B), write(' %]').
portray_bag(bag) :-
write('[% '), write(' %]').
pbag(E, M, B) :-
var(B), !,
pbag(E, M), write(' | '), write(B).
pbag(E, M, bag(F, N, B)) :- !,
pbag(E, M), write(', '), pbag(F, N, B).
pbag(E, M, bag) :- !,
pbag(E, M).
pbag(E, M, B) :-
pbag(E, M), write(' | '), write(B).
pbag(E, M) :-
print(E), write(':'), write(M).
% If bags are to be as useful as lists, we should provide mapping
% predicates similar to those for lists. Hence
% checkbag(Pred, Bag) - applies Pred(Element, Count)
% mapbag(Pred, BagIn, BagOut) - applies Pred(Element, Answer)
% Note that mapbag does NOT give the Count to Pred, but preserves it.
% It wouldn't be hard to apply Pred to four arguments if it wants them.
:- public checkbag/2, mapbag/3.
:- mode checkbag(+, +), mapbag(+, +, -), mapbaglist(+, +, -).
checkbag(Pred, bag(E, M, B)) :-
apply(Pred, [E, M]),
checkbag(Pred, B).
checkbag(Pred, bag).
mapbag(Pred, BagIn, BagOut) :-
mapbaglist(Pred, BagIn, Listed),
keysort(Listed, Sorted),
bagform(Sorted, BagOut).
mapbaglist(Pred, bag(E, M, B), [R-M|L]) :-
apply(Pred, [E, R]),
mapbaglist(Pred, B, L).
mapbaglist(Pred, bag, []).
:- public bag_to_list/2.
:- mode bag_to_list(+, -), bag_to_list(+, +, -, -).
bag_to_list(bag(E, M, B), R) :-
bag_to_list(M, E, L, R),
bag_to_list(B, L).
bag_to_list(bag, []).
bag_to_list(0, E, L, L) :- !.
bag_to_list(M, E, L, [E|R]) :-
N is M-1,
bag_to_list(N, E, L, R).
:- public list_to_bag/2.
:- mode list_to_bag(+, -), bagform(+, -), bagform(?, +, -, +, -).
:- mode addkeys(+, -). % hack to circumvent 'sort' nastiness
list_to_bag(L, B) :-
addkeys(L, K),
keysort(K, S),
bagform(S, B).
addkeys([Head|Tail], [Head-1|Rest]) :-
addkeys(Tail, Rest).
addkeys([], []).
bagform([], bag) :- !.
bagform(List, bag(E, M, B)) :-
bagform(E, List, Rest, 0, M), !,
bagform(Rest, B).
bagform(Head, [Head-N|Tail], Rest, K, M) :-!,
L is K+N,
bagform(Head, Tail, Rest, L, M).
bagform(Head, Rest, Rest, M, M).
:- public bag_to_set/2.
:- mode bag_to_set(+, -).
bag_to_set(bag(E, M, B), [E|S]) :-
bag_to_set(B, S).
bag_to_set(bag, []).
/* There are two versions of the routines member, bagmax, and bagmin.
The slow versions, which are commented out, try to allow for the
possibility that distinct elements in the bag might unify, while
the faster routines assume that all elements are ground terms.
:- public member/3.
:- mode member(?, -, +), member(+, +, +, -).
member(E, M, bag(E, K, B)) :-
member(B, E, K, M).
member(E, M, bag(_, _, B)) :-
member(E, M, B).
member(bag(E, L, B), E, K, M) :- !,
N is K+L,
member(B, E, N, M).
member(bag(_, _, B), E, K, M) :-
member(B, E, K, M).
member(bag, E, M, M).
:- public bagmax/2, bagmin/2.
:- mode bagmax(+, ?), bagmin(+, ?).
% These routines are correct, but Oh, so costly!
bagmax(B, E) :-
member(E, M, B),
\+ (member(F, N, B), N > M).
bagmin(B, E) :-
member(E, M, B),
\+ (member(F, N, B), N < M).
*//* The faster versions follow */
:- public member/3.
:- mode member(?, ?, +).
member(Element, Multiplicity, bag(Element, Multiplicity, _)).
member(Element, Multiplicity, bag(_, _, Bag)) :-
member(Element, Multiplicity, Bag).
:- public bagmax/2, bagmin/2.
:- mode bagmax(+, -), bagmin(+, -), bag_scan(+, +, +, -, +).
bagmax(bag(E, M, B), Emax) :-
bag_scan(B, E, M, Emax, >).
bagmin(bag(E, M, B), Emin) :-
bag_scan(B, E, M, Emin, <).
bag_scan(bag(Eb,Mb,B), Ei, Mi, Eo, C) :-
compare(C, Mb, Mi), !,
bag_scan(B, Eb, Mb, Eo, C).
bag_scan(bag(Eb,Mb,B), Ei, Mi, Eo, C) :-
bag_scan(B, Ei, Mi, Eo, C).
/* bag_scan(bag(Eb,Mb,B), Ei, Mi, Eo, C) :-
bag_scan(B, Eb, Mb, Eo, C). % for all extrema
*/ bag_scan(bag, Ei, Mi, Ei, C).
:- public length/3.
:- mode length(+, -, -), length(+, +, -, +, -).
length(B, BL, SL) :-
length(B, 0, BL, 0, SL).
length(bag(_,M,B), BA, BL, SA, SL) :-
BB is BA+M, SB is SA+1,
length(B, BB, BL, SB, SL).
length(bag, BL, BL, SL, SL).
% sub_bag, if it existed, could be used two ways: to test whether one bag
% is a sub_bag of another, or to generate all the sub_bags. The two uses
% need different implementations.
:- public make_sub_bag/2.
:- mode make_sub_bag(+, -), countdown(+, -).
make_sub_bag(bag(E, M, B), bag(E, N, C)) :-
countdown(M, N),
make_sub_bag(B, C).
make_sub_bag(bag(E, M, B), C) :-
make_sub_bag(B, C).
make_sub_bag(bag, bag).
countdown(M, M).
countdown(M, N) :-
M > 1, K is M-1,
countdown(K, N).
:- public test_sub_bag/2.
:- mode test_sub_bag(+, +), test_sub_bag(+, +, +, +, +, +, +).
test_sub_bag(bag(E1, M1, B1), bag(E2, M2, B2)) :-
compare(C, E1, E2),
test_sub_bag(C, E1, M1, B1, E2, M2, B2).
test_sub_bag(bag, Bag).
test_sub_bag(>, E1, M1, B1, E2, M2, B2) :-
test_sub_bag(bag(E1, M1, B1), B2).
test_sub_bag(=, E1, M1, B1, E1, M2, B2) :-
M1 =< M2,
test_sub_bag(B1, B2).
:- public bag_union/3.
:- mode bag_union(+, +, -), bag_union(+, +, +, +, +, +, +, -).
bag_union(bag(E1, M1, B1), bag(E2, M2, B2), B3) :-
compare(C, E1, E2), !,
bag_union(C, E1, M1, B1, E2, M2, B2, B3).
bag_union(bag, Bag, Bag) :- !.
bag_union(Bag, bag, Bag).
bag_union(<, E1, M1, B1, E2, M2, B2, bag(E1, M1, B3)) :-
bag_union(B1, bag(E2, M2, B2), B3).
bag_union(>, E1, M1, B1, E2, M2, B2, bag(E2, M2, B3)) :-
bag_union(bag(E1, M1, B1), B2, B3).
bag_union(=, E1, M1, B1, E1, M2, B2, bag(E1, M3, B3)) :-
M3 is M1+M2,
bag_union(B1, B2, B3).
:- public bag_inter/3.
:- mode bag_inter(+, +, -), bag_inter(+, +, +, +, +, +, +, -).
bag_inter(bag(E1, M1, B1), bag(E2, M2, B2), B3) :-
compare(C, E1, E2), !,
bag_inter(C, E1, M1, B1, E2, M2, B2, B3).
bag_inter(B1, B2, bag).
bag_inter(<, E1, M1, B1, E2, M2, B2, B3) :-
bag_inter(B1, bag(E2, M2, B2), B3).
bag_inter(>, E1, M1, B1, E2, M2, B2, B3) :-
bag_inter(bag(E1, M1, B1), B2, B3).
bag_inter(=, E1, M1, B1, E1, M2, B2, bag(E1, M3, B3)) :-
( M1 < M2, M3 = M1 ; M3 = M2 ), !,
bag_inter(B1, B2, B3).
/* Sorted list of Bag Utilities:
bag_inter(+Bag1, +Bag2, -Inter)
bag_to_list(+Bag, -List)
bag_to_set(+Bag, -SetList)
bag_union(+Bag1, +Bag2, -Union)
bagmax(+Bag, ?Elem)
bagmin(+Bag, ?Elem)
checkbag(+Pred, +Bag)
is_bag(+Bag)
length(+Bag, -Total, -Distinct)
list_to_bag(+List, -Bag)
make_sub_bag(+Bag, -SubBag)
mapbag(+Pred, +BagIn, -BagOut)
member(?Elem, -Count, +Bag)
portray_bag(+Bag)
test_sub_bag(+SubBag, +Bag)
End of List */
More information about the Comp.sources.unix
mailing list