I agree that partial evaluation is a key, about which I know only partially though.
BTW, the following term_expansion
rule for maplist_rec/3
, a recursive version of maplist/3,
displays this for maplist_rec(plus(1), [0,1,2], Out))
:
% ?- show(maplist_rec(plus(1), [0,1,2], Out)).
%@ pac:pac#12([0,1,2],_), where
%@ pac:pac#12([],[]):-true
%@ pac:pac#12([A|B],[C|D]):-(pac:is_list(A)->pac:pac#12(A,C);pac:plus(1,A,C)),pac:pac#12(B,D)
%@ Out = _.
It seems that my pac library already does something like āpartial evaluationā partially. Is it right ?
Here is a simple statistic for maplist_rec(plus(1), Ks, Out))
, where Ks is a list of length 1000, and each element of Ks is a list of length 1000.
% ?- N = 1000, K=1000, numlist(1, N, Ns), length(Ks, K),
% maplist(=(Ns), Ks),
% time(maplist_rec(plus(1), Ks, Out)).
%@ % 4,003,002 inferences, 0.136 CPU in 0.146 seconds (93% CPU, 29343004 Lips)
%@ N = K, K = 1000,
%@ Ns = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
%@ Ks = [[1, 2, 3, 4, 5, 6, 7, 8|...], [1, 2, 3, 4, 5, 6, 7|...], [1, 2, 3, 4, 5, 6|...], [1, 2, 3, 4, 5|...], [1, 2, 3, 4|...], [1, 2, 3|...], [1, 2|...], [1|...], [...|...]|...],
%@ Out = [[2, 3, 4, 5, 6, 7, 8, 9|...], [2, 3, 4, 5, 6, 7, 8|...], [2, 3, 4, 5, 6, 7|...], [2, 3, 4, 5, 6|...], [2, 3, 4, 5|...], [2, 3, 4|...], [2, 3|...], [2|...], [...|...]|...].
etc(maplist_rec, [F|As], _, meta:G, P, P):- var(F), !,
complete_args(maplist_rec, [F|As], G).
etc(maplist_rec, [F|As], M, G, P, Q):-
expand_arg(F, M, F0, P, P0),
term_variables(F0, Vs),
expand_core(
mrec(Vs, [ Main = pred(
[[], []]
& ([[X|Xs], [Y|Ys]]:-
(( is_list(X)
-> call(Main, X, Y)
; call(F0, X, Y)
),
call(Main, Xs, Ys)))
)
]),
M, G0, P0, Q),
complete_args(G0, As, G).
EDIT 2021/1/3
is_list/1 is expensive !
% ?- N is 10^7, length(X, N), time(repeat(1000, is_list(X))).
%@ % 2,001 inferences, 25.174 CPU in 25.178 seconds (100% CPU, 79 Lips)
%@ N = 10000000,
%@ X = [_2438, _2444, _2450, _2456, _2462, _2468, _2474, _2480, _2486|...].
EDIT 2021/1/4
It seems that is_list/1 is not prepared as a fast type checker like integer/1 and atom/1. So it may not be good to use is_list/1 as follows, provided that X has chance to be bound to a long list or cyclic term.
f(X):- integer(X),!, ā¦
f(X):- atom((X),!, ā¦
f(X):- is_list(X), !, ā¦
ā¦
help(is_list) says:
is_list(+Term)
True if Term is bound to the empty list ([]) or a compound term with nameā
[|]ā and arity 2 and the second argument is a list. This predicate acts as
if defined by the definition below on acyclic terms. The implementation
safely fails if Term represents a cyclic list.
is_list(X) :-
var(X), !,
fail.
is_list([]).
is_list([_|T]) :-
is_list(T).
EDIT 2021/1/4
is_list(X)
compared with (X=[_|_]; X =[])
. So, I should not use is_list/1
for merely checking functor(X, '[|]', 2)
.
% ?- N is 10^7, length(X, N), time(repeat(1000, (X=[_|_]; X =[]))).
%@ % 1,000 inferences, 0.166 CPU in 0.359 seconds (46% CPU, 6014 Lips)
%@ N = 10000000,
%@ X = [_238, _2888, _2894, _2900, _2906, _2912, _2918, _2924, _2930|...] .