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/pl/setof.yap
Vítor Santos Costa a8c77d2609 fix string_concat/2
2014-04-09 12:39:29 +01:00

203 lines
5.2 KiB
Prolog

/*************************************************************************
* *
* YAP Prolog %W% %G%
* *
* Yap Prolog was developed at NCCUP - Universidade do Porto *
* *
* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 *
* *
**************************************************************************
* *
* File: setof.pl *
* Last rev: *
* mods: *
* comments: set predicates *
* *
*************************************************************************/
:- system_module( '$_setof', [(^)/2,
all/3,
bagof/3,
findall/3,
findall/4,
setof/3], []).
:- use_system_module( '$_boot', ['$catch'/3]).
:- use_system_module( '$_errors', ['$do_error'/2]).
% The "existential quantifier" symbol is only significant to bagof
% and setof, which it stops binding the quantified variable.
% op(200, xfy, ^) is defined during bootstrap.
% this is used by the all predicate
:- op(50,xfx,same).
_^Goal :-
'$execute'(Goal).
% findall/3 is a simplified version of bagof which has an implicit
% existential quantifier on every variable.
findall(Template, Generator, Answers) :-
( '$is_list_or_partial_list'(Answers) ->
true
;
'$do_error'(type_error(list,Answers), findall(Template, Generator, Answers))
),
'$findall'(Template, Generator, [], Answers).
% If some answers have already been found
findall(Template, Generator, Answers, SoFar) :-
'$findall'(Template, Generator, SoFar, Answers).
% starts by calling the generator,
% and recording the answers
'$findall'(Template, Generator, SoFar, Answers) :-
nb:nb_queue(Ref),
(
'$execute'(Generator),
nb:nb_queue_enqueue(Ref, Template),
fail
;
nb:nb_queue_close(Ref, Answers, SoFar)
).
% findall_with_key is very similar to findall, but uses the SICStus
% algorithm to guarantee that variables will have the same names.
%
'$findall_with_common_vars'(Template, Generator, Answers) :-
nb:nb_queue(Ref),
(
'$execute'(Generator),
nb:nb_queue_enqueue(Ref, Template),
fail
;
nb:nb_queue_close(Ref, Answers, []),
'$collect_with_common_vars'(Answers, _)
).
'$collect_with_common_vars'([], _).
'$collect_with_common_vars'([Key-_|Answers], VarList) :-
'$variables_in_term'(Key, _, VarList),
'$collect_with_common_vars'(Answers, VarList).
% This is the setof predicate
setof(Template, Generator, Set) :-
( '$is_list_or_partial_list'(Set) ->
true
;
'$do_error'(type_error(list,Set), setof(Template, Generator, Set))
),
'$bagof'(Template, Generator, Bag),
'$sort'(Bag, Set).
% And this is bagof
% Either we have excess of variables
% and we need to find the solutions for each instantiation
% of these variables
bagof(Template, Generator, Bag) :-
( '$is_list_or_partial_list'(Bag) ->
true
;
'$do_error'(type_error(list,Bag), bagof(Template, Generator, Bag))
),
'$bagof'(Template, Generator, Bag).
'$bagof'(Template, Generator, Bag) :-
'$free_variables_in_term'(Template^Generator, StrippedGenerator, Key),
%format('TemplateV=~w v=~w ~w~n',[TemplateV,Key, StrippedGenerator]),
( Key \== '$' ->
'$findall_with_common_vars'(Key-Template, StrippedGenerator, Bags0),
'$keysort'(Bags0, Bags),
'$pick'(Bags, Key, Bag)
;
'$findall'(Template, StrippedGenerator, [], Bag0),
Bag0 \== [],
Bag = Bag0
).
% picks a solution attending to the free variables
'$pick'([K-X|Bags], Key, Bag) :-
'$parade'(Bags, K, Bag1, Bags1),
'$decide'(Bags1, [X|Bag1], K, Key, Bag).
'$parade'([K-X|L1], Key, [X|B], L) :- K == Key, !,
'$parade'(L1, Key, B, L).
'$parade'(L, _, [], L).
%
% The first argument to decide gives if solutions still left;
% The second gives the solution currently found;
% The third gives the free variables that are supposed to be bound;
% The fourth gives the free variables being currently used.
% The fifth outputs the current solution.
%
'$decide'([], Bag, Key0, Key, Bag) :- !,
Key0=Key.
'$decide'(_, Bag, Key, Key, Bag).
'$decide'(Bags, _, _, Key, Bag) :-
'$pick'(Bags, Key, Bag).
% as an alternative to setof you can use the predicate all(Term,Goal,Solutions)
% But this version of all does not allow for repeated answers
% if you want them use findall
all(T, G same X,S) :- !, all(T same X,G,Sx), '$$produce'(Sx,S,X).
all(T,G,S) :-
'$init_db_queue'(Ref),
( '$catch'(Error,'$clean_findall'(Ref,Error),_),
'$execute'(G),
'$db_enqueue'(Ref, T),
fail
;
'$$set'(S,Ref)
).
% $$set does its best to preserve space
'$$set'(S,R) :-
'$$build'(S0,_,R),
S0 = [_|_],
S = S0.
'$$build'(Ns,S0,R) :- '$db_dequeue'(R,X), !,
'$$build2'(Ns,S0,R,X).
'$$build'([],_,_).
'$$build2'([X|Ns],Hash,R,X) :-
'$$new'(Hash,X), !,
'$$build'(Ns,Hash,R).
'$$build2'(Ns,Hash,R,_) :-
'$$build'(Ns,Hash,R).
'$$new'(V,El) :- var(V), !, V = n(_,El,_).
'$$new'(n(R,El0,L),El) :-
compare(C,El0,El),
'$$new'(C,R,L,El).
'$$new'(=,_,_,_) :- !, fail.
'$$new'(<,R,_,El) :- '$$new'(R,El).
'$$new'(>,_,L,El) :- '$$new'(L,El).
'$$produce'([T1 same X1|Tn],S,X) :- '$$split'(Tn,T1,X1,S1,S2),
( S=[T1|S1], X=X1;
!, produce(S2,S,X) ).
'$$split'([],_,_,[],[]).
'$$split'([T same X|Tn],T,X,S1,S2) :- '$$split'(Tn,T,X,S1,S2).
'$$split'([T1 same X|Tn],T,X,[T1|S1],S2) :- '$$split'(Tn,T,X,S1,S2).
'$$split'([T1|Tn],T,X,S1,[T1|S2]) :- '$$split'(Tn,T,X,S1,S2).