View source with formatted comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  1985-2014, University of Amsterdam,
    7                              VU University Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   37Copyright notes: findall/3, bagof/3 and setof/3 are part of the standard
   38folklore of Prolog. The core  is  findall/3   based  on  C code that was
   39written for SWI-Prolog. Older versions also used C-based implementations
   40of  bagof/3  and  setof/3.  As   these    proved   wrong,   the  current
   41implementation is modelled  after  an  older   version  of  Yap.  Ulrich
   42Neumerkel fixed the variable preservation of   bagof/3 and setof/3 using
   43an algorithm also found in  Yap  6.3,   where  it  is claimed: "uses the
   44SICStus algorithm to guarantee that variables will have the same names".
   45- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
   46
   47:- module('$bags',
   48          [ findall/3,                  % +Templ, :Goal, -List
   49            findall/4,                  % +Templ, :Goal, -List, +Tail
   50            findnsols/4,                % +Count, +Templ, :Goal, -List
   51            findnsols/5,                % +Count, +Templ, :Goal, -List, +Tail
   52            bagof/3,                    % +Templ, :Goal, -List
   53            setof/3                     % +Templ, :Goal, -List
   54          ]).   55
   56:- meta_predicate
   57    findall(?, 0, -),
   58    findall(?, 0, -, ?),
   59    findnsols(+, ?, 0, -),
   60    findnsols(+, ?, 0, -, ?),
   61    bagof(?, ^, -),
   62    setof(?, ^, -).   63
   64:- noprofile((
   65        findall/4,
   66        findall/3,
   67        findnsols/4,
   68        findnsols/5,
   69        bagof/3,
   70        setof/3,
   71        findall_loop/4)).   72
   73:- '$iso'((findall/3,
   74           bagof/3,
   75           setof/3)).   76
   77%!  findall(-Var, +Goal, -Bag) is det.
   78%!  findall(-Var, +Goal, -Bag, +Tail) is det.
   79%
   80%   Bag holds all alternatives for Var  in  Goal.   Bag  might  hold
   81%   duplicates.   Equivalent  to bagof, using the existence operator
   82%   (^) on all free variables of Goal.  Succeeds with Bag  =  []  if
   83%   Goal fails immediately.
   84%
   85%   The  findall/4  variation  is  a    difference-list  version  of
   86%   findall/3.
   87
   88findall(Templ, Goal, List) :-
   89    findall(Templ, Goal, List, []).
   90
   91findall(Templ, Goal, List, Tail) :-
   92    setup_call_cleanup(
   93        '$new_findall_bag',
   94        findall_loop(Templ, Goal, List, Tail),
   95        '$destroy_findall_bag').
   96
   97findall_loop(Templ, Goal, List, Tail) :-
   98    (   Goal,
   99        '$add_findall_bag'(Templ)   % fails
  100    ;   '$collect_findall_bag'(List, Tail)
  101    ).
  102
  103%!  findnsols(+Count, @Template, :Goal, -List) is nondet.
  104%!  findnsols(+Count, @Template, :Goal, -List, ?Tail) is nondet.
  105%
  106%   True when List is the next chunk of maximal Count instantiations
  107%   of Template that reprensents a solution of Goal.  For example:
  108%
  109%     ==
  110%     ?- findnsols(5, I, between(1, 12, I), L).
  111%     L = [1, 2, 3, 4, 5] ;
  112%     L = [6, 7, 8, 9, 10] ;
  113%     L = [11, 12].
  114%     ==
  115%
  116%   @compat Ciao, but the SWI-Prolog version is non-deterministic.
  117%   @error  domain_error(not_less_than_zero, Count) if Count is less
  118%           than 0.
  119%   @error  type_error(integer, Count) if Count is not an integer.
  120
  121findnsols(Count, Template, Goal, List) :-
  122    findnsols(Count, Template, Goal, List, []).
  123
  124findnsols(Count, Template, Goal, List, Tail) :-
  125    integer(Count),
  126    !,
  127    findnsols2(count(Count), Template, Goal, List, Tail).
  128findnsols(Count, Template, Goal, List, Tail) :-
  129    Count = count(Integer),
  130    integer(Integer),
  131    !,
  132    findnsols2(Count, Template, Goal, List, Tail).
  133findnsols(Count, _, _, _, _) :-
  134    '$type_error'(integer, Count).
  135
  136findnsols2(Count, Template, Goal, List, Tail) :-
  137    nsols_count(Count, N), N > 0,
  138    !,
  139    copy_term(Template+Goal, Templ+G),
  140    setup_call_cleanup(
  141        '$new_findall_bag',
  142        findnsols_loop(Count, Templ, G, List, Tail),
  143        '$destroy_findall_bag').
  144findnsols2(Count, _, _, List, Tail) :-
  145    nsols_count(Count, 0),
  146    !,
  147    Tail = List.
  148findnsols2(Count, _, _, _, _) :-
  149    nsols_count(Count, N),
  150    '$domain_error'(not_less_than_zero, N).
  151
  152findnsols_loop(Count, Templ, Goal, List, Tail) :-
  153    nsols_count(Count, FirstStop),
  154    State = state(FirstStop),
  155    (   call_cleanup(Goal, Det=true),
  156        '$add_findall_bag'(Templ, Found),
  157        Det \== true,
  158        arg(1, State, Found),
  159        '$collect_findall_bag'(List, Tail),
  160        (   '$suspend_findall_bag'
  161        ;   nsols_count(Count, Incr),
  162            NextStop is Found+Incr,
  163            nb_setarg(1, State, NextStop),
  164            fail
  165        )
  166    ;   '$collect_findall_bag'(List, Tail)
  167    ).
  168
  169nsols_count(count(N), N).
  170
  171%!  bagof(+Var, +Goal, -Bag) is semidet.
  172%
  173%   Implements Clocksin and  Melish's  bagof/3   predicate.  Bag  is
  174%   unified with the alternatives of Var  in Goal, Free variables of
  175%   Goal are bound,  unless  asked  not   to  with  the  existential
  176%   quantifier operator (^).
  177
  178bagof(Templ, Goal0, List) :-
  179    '$free_variable_set'(Templ^Goal0, Goal, Vars),
  180    (   Vars == v
  181    ->  findall(Templ, Goal, List),
  182        List \== []
  183    ;   findall(Vars-Templ, Goal, Answers),
  184        bind_bagof_keys(Answers,_),
  185        keysort(Answers, Sorted),
  186        pick(Sorted, Vars, List)
  187    ).
  188
  189%!  bind_bagof_keys(+VarsTemplPairs, -SharedVars)
  190%
  191%   Establish a canonical binding  of   the  _vars_ structures. This
  192%   code   was   added    by    Ulrich     Neumerkel    in    commit
  193%   1bf9e87900b3bbd61308e80a784224c856854745.
  194
  195bind_bagof_keys([], _).
  196bind_bagof_keys([W-_|WTs], Vars) :-
  197    term_variables(W, Vars, _),
  198    bind_bagof_keys(WTs, Vars).
  199
  200pick(Bags, Vars1, Bag1) :-
  201    pick_first(Bags, Vars0, Bag0, RestBags),
  202    select_bag(RestBags, Vars0, Bag0, Vars1, Bag1).
  203
  204select_bag([], Vars0, Bag0, Vars1, Bag1) :-   % last one: deterministic
  205    !,
  206    Vars0 = Vars1,
  207    Bag0 = Bag1.
  208select_bag(_, Vars, Bag, Vars, Bag).
  209select_bag(RestBags, _, _, Vars1, Bag1) :-
  210    pick(RestBags, Vars1, Bag1).
  211
  212%!  pick_first(+Bags, +Vars, -Bag1, -RestBags) is semidet.
  213%
  214%   Pick the first result-bag from the   list  of Templ-Answer. Note
  215%   that we pick all elements that are  equal under =@=, but because
  216%   the variables in the witness are canonized this is the same as ==.
  217%
  218%   @param Bags     List of Templ-Answer
  219%   @param Vars     Initial Templ (for rebinding variables)
  220%   @param Bag1     First bag of results
  221%   @param RestBags Remaining Templ-Answer
  222
  223pick_first([Vars-Templ|T0], Vars, [Templ|T], RestBag) :-
  224    pick_same(T0, Vars, T, RestBag).
  225
  226
  227pick_same([V-H|T0], Vars, [H|T], Bag) :-
  228    V == Vars,
  229    !,
  230    pick_same(T0, Vars, T, Bag).
  231pick_same(Bag, _, [], Bag).
  232
  233
  234%!  setof(+Var, +Goal, -Set) is semidet.
  235%
  236%   Equivalent to bagof/3, but sorts the   resulting bag and removes
  237%   duplicate answers. We sort  immediately   after  the  findall/3,
  238%   removing duplicate Templ-Answer pairs early.
  239
  240setof(Templ, Goal0, List) :-
  241    '$free_variable_set'(Templ^Goal0, Goal, Vars),
  242    (   Vars == v
  243    ->  findall(Templ, Goal, Answers),
  244        Answers \== [],
  245        sort(Answers, List)
  246    ;   findall(Vars-Templ, Goal, Answers),
  247        (   ground(Answers)
  248        ->  sort(Answers,Sorted),
  249            pick(Sorted,Vars,List)
  250        ;   bind_bagof_keys(Answers,_VDict),
  251            sort(Answers, Sorted),
  252            pick(Sorted, Vars, Listu),
  253            sort(Listu,List) % Listu ordering may be nixed by Vars
  254        )
  255    )