Re: findall with limited solution count



An elegant way to handle this is to have an answer iterator as part of
the language. Findall/3 and its controlled versions can then be derived
naturally (see http://www.binnetcorp.com/kprolog/fluents.html, also a
paper in CL2000).

BinProlog and Jinni Prolog (see http://www.binnetcorp.com) implement
such iterators as part of the "multiple Prolog engines" API.

The most efficient way to provide such an iterator (an easy addition to
any Prolog!) is a weak variant of setarg (BinProlog and Jinni Prolog
have it as change_arg/3), that simply updates in place a _constant_
argument with a new _constant_ (no trailing or value trailing needed).

Based on that, one can build a complete API controlling sequences of
answers, as follows:

% answers G as far as constraint C holds
while(C,G):-G,(C->true;!,fail).

skip_until(C,G):-G,(C->fail;!).

skip_when(C,G):-G,(C->fail;true).

% gives only the N-th answer of G
nth_answer(N,G):-N1 is N-1,Max=s(N1),skip_until(has_fuel(Max),G).

% generates at most the first N answers of G
take_at_most(N,G):-Max=s(N),while(has_fuel(Max),G).

% drops at least the first N answers of G
drop_at_least(N,G):-Max=s(N),skip_when(has_fuel(Max),G).

% re-entrant in-place counter
has_fuel(Max):-arg(1,Max,N),N>0,N1 is N-1,change_arg(1,Max,N1).

% answer_stream to list converters
find_while(C,X,G,Xs):-findall(X,while(C,G),Xs).

find_at_most(N,X,G,Xs):-findall(X,take_at_most(N,G),Xs).

all_but_at_least(N,X,G,Xs):-findall(X,drop_at_least(N,G),Xs).

% signals error if G has more than one answer
det_call(G):-find_at_most(2,G,G,Gs),!,
( Gs=[]->fail
; Gs=[G]->true
; % member(G,Gs),
errmes('expected to be deterministic',G)
).

% SWI, SICStus, Mercury compatible if/3 construct
% which backtracks over Cond (SWI uses *-> for this)

if_any(Cond,Then,Else):-
Ctr=s(0),
( Cond,change_arg(1,Ctr,1),Then
; arg(1,Ctr,0),Else
).

Paul Tarau
http://www.binnetcorp.com

.