I came up with a findall/3 that doesn’t use call_cleanup/2.
The findall2/3
realization uses nb_linkarg/3 and duplicate_term/2.
Its not as fast as the bundled findall/3. Lets take these test cases:
test :-
between(1,1000,_), findall(Y-X-Y, between(1,1000,X), _), fail; true.
test2 :-
between(1,1000,_), findall2(Y-X-Y, between(1,1000,X), _), fail; true.
I get these benchmark results:
/* SWI-Prolog 9.3.14 */
?- time(test).
% 2,008,999 inferences, 0.125 CPU in 0.172 seconds (73% CPU, 6071992 Lips)
true.
?- time(test2).
% 6,007,999 inferences, 0.422 CPU in 0.427 seconds (99% CPU, 14241183 Lips)
true.
But I wonder was it safe to use nb_linkarg/3 and duplicate_term/2 ?
And can such a findall2/3
be made faster somehow? The source
code, just a single linked list with a known end element. The end
gets updated and the known end itself changes as well:
findall2(Template, Goal, List) :-
sys_find2_init(State),
(Goal, sys_find2_next(Template, State), fail; true),
sys_find2_fini(State, List).
sys_find2_init(X) :-
X = v(_,_),
C = [-|_],
nb_linkarg(1, X, C),
nb_linkarg(2, X, C).
sys_find2_next(T, X) :-
C = [_|_],
duplicate_term(T, H),
nb_linkarg(1, C, H),
arg(1, X, J), %%% known end cell
nb_linkarg(2, J, C),
nb_linkarg(1, X, C).
sys_find2_fini(v(C,D), L) :-
nb_linkarg(2, C, []),
arg(2, D, L).