11.2.2 bagof/3

The findall/3 predicate is useful, but in certain respects it is rather crude. For example, suppose we pose the query

findall(Child,descend(Mother,Child),List).

We get the response

Child = _6947  
Mother = _6951  
List = [charlotte,caroline,laura,rose,caroline,laura,rose,laura,rose,rose] 

Now, this is correct, but sometimes it would be useful if we had a separate list for each of the different instantiations of Mother.

This is what bagof lets us do. If we pose the query

bagof(Child,descend(Mother,Child),List).

we get the response

Child = _7736  
Mother = caroline  
List = [laura,rose] ;
 
Child = _7736  
Mother = charlotte  
List = [caroline,laura,rose] ;
 
Child = _7736  
Mother = laura  
List = [rose] ;
 
Child = _7736  
Mother = martha  
List = [charlotte,caroline,laura,rose] ;
 
no

That is, bagof is more finegrained than findall, it gives us the opportunity to extract the information we want in a more structured way. Moreover, bagof can also do the same job as findall, with the help of a special piece of syntax. If we pose the query

bagof(Child,Mother ^ descend(Mother,Child),List).

This says: give me a list of all the values of Child such that descend(Mother,Child), and put the result in a list, but don't worry about generating a separate list for each value of Mother. So posing this query yields:

Child = _7870  
Mother = _7874  
List = [charlotte,caroline,laura,rose,caroline,laura,rose,laura,rose,rose]

Note that this is exactly the response that findall would have given us. Still, if this is the kind of query you want to make (and it often is) it's simpler to use findall, because then you don't have to bother explicitly write down the conditions using ^.

Further, there is one important difference between findall and bagof, and that is that bagof fails if the goal that's specified in its second argument is not satisfied (remember, that findall returns the empty list in such a case). So the query bagof(X,descend(mary,X),Z) yields no.

One final remark. Consider again the query

bagof(Child,descend(Mother,Child),List).

As we saw above, this has four solutions. But, once again, Prolog generates them one by one. Wouldn't it be nice if we could collect them all into one list?

And, of course, we can. The simplest way is to use findall. The query

findall(List,bagof(Child,descend(Mother,Child),List),Z).

collects all of bagof's responses into one list:

List = _8293  
Child = _8297  
Mother = _8301  
Z = [[laura,rose],[caroline,laura,rose],[rose],
                  [charlotte,caroline,laura,rose]] 

Another way to do it is with bagof:

bagof(List,Child ^ Mother ^ bagof(Child,descend(Mother,Child),List),Z).
 
List = _2648  
Child = _2652  
Mother = _2655  
Z = [[laura,rose],[caroline,laura,rose],[rose],
                  [charlotte,caroline,laura,rose]]

Now, this may not be the sort of thing you need to do very often, but it does show the flexibility and power offered by these predicates.


Patrick Blackburn, Johan Bos and Kristina Striegnitz
Version 1.2.5 (20030212)