Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

findall/3 creates new, unrelated variables in its resulting list

?- permutation([A,B,C],Z).
Z = [A, B, C] ;
Z = [A, C, B] ;
Z = [B, A, C] ;
Z = [B, C, A] ;
Z = [C, A, B] ;
Z = [C, B, A] ;
false.

Makes sense. I can work on a permutation of [A,B,C] and that permutation contains the same elements as in [A,B,C], so everything I do to those elements will apply to my original list.

Now:

?- findall(X, permutation([A,B,C], X), Z).
Z = [[_G1577, _G1580, _G1583], [_G1565, _G1568, _G1571], [_G1553, _G1556, _G1559], [_G1541, _G1544, _G1547], [_G1529, _G1532, _G1535], [_G1517, _G1520, _G1523]].

Why?? Why is findall/3 giving me lists which contain completely unrelated variables, instead of A,B,C? The lists in Z are not even related to each other, so really the result I get is just 6 random lists of length 3, which is totally not what I queried.

With this behavior we get ridiculous results like this:

?- findall(X, permutation([A,B,C],X), Z), A = 1.
A = 1,
Z = [[_G1669, _G1672, _G1675], [_G1657, _G1660, _G1663], [_G1645, _G1648, _G1651], [_G1633, _G1636, _G1639], [_G1621, _G1624, _G1627], [_G1609, _G1612, _G1615]].

Which makes no sense from a logical standpoint.

I understand that findall/3 is not really a relational, pure logic predicate but I don't see how this justifies the behavior shown here.

My questions are therefore:

  • Why was this behavior chosen for the predicate?

  • Are there common situations where this behavior is actually preferable to the one I want?

  • How to implement a version of findall/3 with the behavior I want?

like image 959
Fatalize Avatar asked Jun 23 '17 18:06

Fatalize


3 Answers

Why was this behavior chosen for the predicate?

findall/3 is a highly primitive built-in predicate that is relatively easy to implement and that does not address all the nitty-gritty details you are interested in. At least it is reentrant - thus can be used recursively.

Historically, DEC10 Prolog did not document findall/3. That is, neither in 1978 nor 1984. The 1984 version did however provide setof/3 which internally uses a findall-like predicate. Implementing it in ISO-Prolog (without findall/3) is relatively tricky since you have to handle errors and nesting. Many implementations rely on implementation specific primitives.

Are there common situations where this behavior is actually preferable to the one I want?

Findall succeeds if there is no solution whereas both setof/3 and bagof/3 simply fail. This might be a reason to prefer it. At least some more sophisticated constructs than those are needed which are most probably built based on findall.

It gets pretty messy in the presence of constraints. In fact, it is so messy, that as of the current point in time I am still unaware of an implementation that would deal in a reasonable manner with clpfd-constraints in this very situation. Think of:

?- findall(A, (A in 1..3 ; A in 5..7), As).

Here, SWI copies constraints, where SICStus does not copy them permitting you thus to use it as building-block for a more sophisticated implementation.

How to implement a version of findall/3 with the behavior I want?

First, consider setof/3 and bagof/3 (here). Maybe you are happy with them already - as long as no constraints are involved...

like image 179
false Avatar answered Oct 17 '22 17:10

false


A solution to your last question.

?- setof(X,permutation([A,B,C],X),Z).
Z = [[A, B, C], [A, C, B], [B, A, C], [B, C, A], [C, A, B], [C, B, A]].

If we look at the description of findall at sicstus we see

findall(?Template,:Goal,?Bag) ISO Bag is a list of instances of Template in all proofs of Goal found by Prolog. The order of the list corresponds to the order in which the proofs are found. The list may be empty and all variables are taken as being existentially quantified. This means that each invocation of findall/3 succeeds exactly once, and that no variables in Goal get bound. Avoiding the management of universally quantified variables can save considerable time and space.

So I guess the existential quantifying creates this unwanted behaviour of findall.

like image 25
Ben373 Avatar answered Oct 17 '22 17:10

Ben373


?- findall(X, permutation([A,B,C],X), Z), A = 1.

In this query Prolog will find all permutation of the elements on the list [A,B,C], but since Prolog can not instantiate the variables A, B, C, the result will be this that you are getting, the anonymous variable:

Z = [[_G1669, _G1672, _G1675], [_G1657, _G1660, _G1663], [_G1645, _G1648, _G1651], [_G1633, _G1636, _G1639], [_G1621, _G1624, _G1627], [_G1609, _G1612, _G1615]].

On the other hand, if you first instantiate the variables A, B and C, you will get a different result:

?- A=1, B=2, C=3, findall(X, permutation([A,B,C],X), Z).
A = 1,
B = 2,
C = 3,
Z = [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

This didn't happend before in your query findall(X, permutation([A,B,C],X), Z), A = 1. because Prolog will first try to solve the condition findall(X, permutation([A,B,C],X), Z) and then A = 1

like image 3
Yasel Avatar answered Oct 17 '22 18:10

Yasel