I'm using SWI-PROLOG.
Given a list where almost all elements are compound terms like:
?- MyList = [json([a=1, b=2]), json([a=4, b=1]), 999, json([a=7, b=2])].
I want to filter only the compound term elements, so i tried the following:
?- MyList= ..., include(=(json(_)), MyList, FilteredElems).
FilteredElems = [json([a=1, b=2])].
i was hoping to get the following result FilteredElems= [json([a=1, b=2]), json([a=4, b=1]), json([a=7, b=2])] but only the first one is included.
I discovered that after the first call of the predicate, the underscore gets grounded to: [a=1, b=2] value, so the next attempts of succeding the unification with json(_) fail.
Does exists a clean way to solve this? Am i missing something?
To test the grounding of the underscore i used my modified include version:
inc(Goal, List, Included) :-
inc_(List, Goal, Included).
inc_([], _, []).
inc_([X1|Xs1], P, Included) :-
(
writef("Calling call(%t, %t)\n", [P, X1]),
call(P, X1)
-> Included = [X1|Included1],
writef("\tTrue\n")
; Included = Included1,
writef("\tFalse\n")
),
inc_(Xs1, P, Included1).
Heading
I tried with predicate functor/3 and lambda (library yall); in this case the underscore get bounded to different values (in the below examples becomes 1; 1; _ ; 1; 2)
?- MyList = [json(a), json(b), 999, json(c), json(d, 123)], include([E]>>(functor(E,json, A), writef("Arity: %t \n",[A])), MyList, FilteredElems).
Arity: 1
Arity: 1
Arity: 1
Arity: 2
FilteredElems = [json(a), json(b), json(c), json(d, 123)]. % As wanted
All is working as I expect in this case.
The same "problem" still happens when not using lambda; the underscore in this case get bounded after the first call.
test_functor_(FunctorName, Arity, Term):-
writef("Term: %t, Arity: %t\n", [Term, Arity]),
functor(Term, FunctorName, Arity).
?- MyList = [json(a), json(b), 999, json(c), json(d, 123)], include(test_functor_(json, _), MyList, FilteredElems).
Term: json(a), Arity: _8794
Term: json(b), Arity: 1
Term: 999, Arity: 1
Term: json(c), Arity: 1
Term: json(d,123), Arity: 1
MyList = [json(a), json(b), 999, json(c), json(d, 123)],
FilteredElems = [json(a), json(b), json(c)]. % Missing the last element
Can someone explain?
I think you are on the right track!
However, you were not asking one question, but three: (1) Does there exist a clean way to solve this? (2) Am I missing something? (3) Can someone explain?
Let me try to answer them separately...
Regarding (2) and (3) let me point out that you are observing the same effect with
and
In both cases, the anonymous variable stands for one (and only one) value.
For the first query it is
[a=1, b=2], for the second one1.Regarding (1): Yes, lambdas can help by providing "internal" variables, just as you observed.
Alternatively, you can use an auxiliary predicate like so:
is_json(X) :- functor(X,json,_). % "_" will be a fresh variable ... % ... everytime is_json is called!Sample query: