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).
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
?- ..., include(=(json(_)), MyList, FilteredElems).
and
?- ..., include(test_functor_(json,_), MyList, FilteredElems).
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 one 1
.
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:
?- include(is_json,[json(a),json(X),123,foo,foo(2),json(a,b),bar(f),baz(z)],Xs).
Xs = [json(a),json(X),json(a,b)].