I need to iterate over a range of numbers (e.g. using between/3
) from 1 to Length
where Length
is a length of a given list. Iterated number, say N
, is then applied to a predicate, say do_something
until it stops failing. That is, do_something
searches for a correct N
after which all choice points of between/3
must be dropped. However, all choice points of do_something
with proper N
applied must be executed.
Fist attempt was something like this:
% main(+InputList, -N, -OutputList)
main(InputList, N, OutputList) :-
length(InputList, Length),
between(1, Length, N),
% cut all between/3 choice points as soon as Num is instantiated
freeze(Num, !),
do_something(InputList, N, OutputList),
Num is N.
This did not work as freeze/2
doesn't cut between/3
choice points. A version with when/2
did not work either. I gather, it is because both freeze/2
and when/2
are executed in a separate thread and do not affect the main thread where between/3
is.
After a while I ended up with the following code that does what's needed, but is inefficient:
main(InputList, N, OutputList) :-
length (InputList, Length),
between(1, Length, N),
do_something(InputList, N, _),
% cut all prior choice points as soon as proper N is found
!,
% start do_something over!
do_something(InputList, N, OutputList).
Inefficiency is that do_something
's choice point with proper N
is executed twice. First just before cut, and then once again right after it.
Though this solution works in my case, it is not applicable to a generic case when all do_something
choice points must be executed only once.
Note that N
is required to be minimum out of 1..Length
range and is not known in advance. Hence searching for it with do_something
.
Is there a better solution to this? Is there a way to implement a predicate similar to between/3
that can stop when signaled somehow? Could there be a specialized builtin predicate that does what's needed? Any fruitful ideas are highly appreciated.
It turns out that
between/3
is a distraction. We don't require it and thus a simple, efficient, and portable solution is possible:As in Jan’s solution, it does not evaluate all solutions of
do_something/3
before returning the first, neither does it evaluate the predicate twice. But it also doesn’t require the nasty and non-portablenb_setarg/2
predicate tricks.Note that, as Jan remarked, the soft-cut control construct,
*->/2
, or itsif/3
meta-predicate variant, can be found in several Prolog systems (including, in one form or the other, CxProlog, Ciao Prolog, ECLiPSe, GNU Prolog, JIProlog, SICStus Prolog, SWI-Prolog, and YAP).P.S. I keep my first answer as it's more portable and exemplifies a pattern that may be useful for other problems.