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/3is 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/3before returning the first, neither does it evaluate the predicate twice. But it also doesn’t require the nasty and non-portablenb_setarg/2predicate tricks.Note that, as Jan remarked, the soft-cut control construct,
*->/2, or itsif/3meta-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.