Search code examples
prolog

bagof only gives one item, though multiple items fit


This code works as expected:

 ?- bagof(R,member(r(R),[r(a),r(b),r(c),r(d)]),Rs).
Rs = [a, b, c, d].

but a similar call, the one I really want, does not:

?- bagof(R,member(r(_,R),[r(_,a), r(_,b), r(_,c), r(_,d)]),Rs).
Rs = [a] 

; gives me more answers -- but I want [a,b,c,d]. What's my fix?


Solution

  • You need to existentially qualify the arguments that you're not interested in instead of using anonymous variables:

    ?- bagof(R,A^A1^A2^A3^A4^member(r(A,R),[r(A1,a), r(A2,b), r(A3,c), r(A4,d)]),Rs).
    Rs = [a, b, c, d].
    

    This is necessary as bagof/3 (and setof/3) will return a bag (set) of solutions for each instantiation of the free variables (i.e. the variables in the goal that are not in the template). In alternative, you can use the findall/3 predicate (which ignores free variables):

    ?- findall(R,member(r(A,R),[r(A1,a), r(A2,b), r(A3,c), r(A4,d)]),Rs).
    Rs = [a, b, c, d].
    

    But note that findall/3 returns an empty list when there are no solutions while bagof/3 (and setof/3) fail when there are no solutions.

    Another alternative to avoid a long list of existentially qualified variables in calls to bagof/3 and setof/3 is to introduce an auxiliary predicate with a single clause whose head only lists the arguments you're interested in. For example:

    r(R) :-
        member(r(_,R),[r(_,a), r(_,b), r(_,c), r(_,d)]).
    
    ?- bagof(R, r(R), Rs).
    Rs = [a, b, c, d].