0

I'm creating a program which should allow search through a graph, but the function which should return a list of successor nodes is failing when a call to findall/3 evaluates to false. When I try the findall function on its own outside of the find_successors function it works perfectly, but for some reason inside the find_successors function it simply reads false. Stepping through with the graphical debugger I can even see it finding all the solutions. Here's the code:

find_successors(Start, Out) :- 
    entity(Start),
    (findall(X, is_a(Start, X), O), append([], O, OL1); OL1 = []),
    (findall(X, is_a(X, Start), O), OL2 = O; OL2 = []),

    (findall(X, has(Start, X), O), append([], O, OL3); OL3 = []),
    (findall(X, has(X, Start), O), append([], O, OL4); OL4 = []),

    (findall(X, able_to(Start, X), O), append([], O, OL5); OL5 =[]),
    (findall(X, able_to(X, Start), O), append([], O, OL6); OL6 = []),

    (findall(X, used_to(Start, X), O), append([], O, OL7); OL7 = []),
    (findall(X, used_to(X, Start), O), append([], O, OL8); OL8 = []),

    append([OL1, OL2, OL3, OL4, OL5, OL6, OL7, OL8], Out).

entity(wings).
entity(fly).
entity(bird).
entity(legs).
entity(feathers).
entity('body covering').
entity(animal).
entity(dog).
entity(fur).
entity(aves).
entity(reptile).
entity(snake).
entity(scales).

f_is_a(bird, aves).
f_is_a(bird, animal).
f_is_a(snake, reptile).
f_is_a(snake, animal).
f_is_a(dog, mammal).
f_is_a(dog, animal).
f_is_a(feathers, 'body covering').
f_is_a(fur, 'body covering').
f_is_a(mammal, animal).
f_is_a(reptile, animal).
f_is_a(aves, animal).
is_a(X, H) :- !, f_is_a(X, H).
is_a(X, H) :- !, \+f_is_a(X, P), H = X.
is_a(X, H) :- !, is_a(X, P), is_a(P, H).

f_has(bird, wings).
f_has(bird, feathers).
f_has(bird, legs).
f_has(aves, wings).
f_has(aves, feathers).
f_has(aves, legs).
f_has(dog, legs).
f_has(dog, fur).
f_has(mammal, legs).
f_has(mammal, fur).
f_has(snake, scales).
f_has(reptile, scales).
has(X, H) :- !, f_has(X, H).
has(X, H) :- !, \+f_has(X, P), H = X.
has(X, H) :- !, has(X, P), has(P, H).

used_to(wings, fly).
used_to(legs, walk).

able_to(bird, fly).
able_to(bird, walk).
able_to(dog, walk).
able_to(X, Y) :- used_to(X1, Y), has(X, X1).
bendl
  • 1,583
  • 1
  • 18
  • 41

2 Answers2

2

You keep on trying to reuse the same variable, but once a variable is bound, you can't use it again. So all these:

        here              here
         |                  |
         v                  v
(findall(X, is_a(Start, X), O), append([], O, OL1); OL1 = []),
(findall(X, is_a(X, Start), O), OL2 = O; OL2 = []),

(findall(X, has(Start, X), O), append([], O, OL3); OL3 = []),
(findall(X, has(X, Start), O), append([], O, OL4); OL4 = []),

(findall(X, able_to(Start, X), O), append([], O, OL5); OL5 =[]),
(findall(X, able_to(X, Start), O), append([], O, OL6); OL6 = []),

(findall(X, used_to(Start, X), O), append([], O, OL7); OL7 = []),
(findall(X, used_to(X, Start), O), append([], O, OL8); OL8 = []),

And every single one of these lines is very, very strange. I need to break it down to actually figure out what is going on. Taking just one of these:

(   findall(X, used_to(Start, X), O),
    append([], O, OL7)
;   OL7 = []
)

(this, btw, is how you should try to write disjunctions, otherwise they are easy to misread)

append([], A, B) is just the same as A = B.

Then, findall/3 always succeeds, even if there are no solutions; it just gives you an empty list!

?- findall(X, between(2, 1, X), Xs).
Xs = [].

So the whole thing is completely unnecessary, you can just as well throw away everything but the call to findall/3.

Note on the side: the disjunction you are using does not do what you think it does. Here is a small example:

?- ( A = 1 ; A = 2 ).

What do you think happens?

  • Per usual it's always the stupidest mistakes that I'm stuck on for long enough to embarrass myself on stack over flow... But I'm confused about your side note... After running that example it gave me exactly the answer I had expected it to, that is `A=1; A=2` – bendl Dec 04 '16 at 17:58
0

You should propose us a call to find_successors(Start, Out) and say the expected values.

Without it its difficult to say where your code is wrong but... some point in no particular order...

(1) append/3 concatenate unify the third argument with a list obtained concatenating the elements from the first and from the second list; so

append([], O, OL1)

with the first argument without elements in it, unify O with OL1 so is unuseful; you can write all the rows in the form

(findall(X, is_a(Start, X), O), append([], O, OL1); OL1 = []),

as

(findall(X, is_a(Start, X), OL1) ; OL1 = []),

(2) findall/3 return true also when unify the third argument with an empty list (when doesn't find value), so I don't see why do you write

(findall(X, is_a(Start, X), OL1) ; OL1 = []),

when the second part (OL1 = []) is never executed (if I'm not wrong) and when OL1 is unified with [] when findall/3 find nothing; I think you can simply write

findall(X, is_a(Start, X), OL1),

(3) I know only an append with three arguments; so I don't understand the meaning of

append([OL1, OL2, OL3, OL4, OL5, OL6, OL7, OL8], Out)

Your intention was to write

append([], [OL1, OL2, OL3, OL4, OL5, OL6, OL7, OL8], Out)

?

In this case, taking in count (1) and (2), you can write find_successors/2 simply as

find_successors(Start, [OL1, OL2, OL3, OL4, OL5, OL6, OL7, OL8]) :- 
    entity(Start),
    findall(X, is_a(Start, X), OL1),
    findall(X, is_a(X, Start), OL2),
    findall(X, has(Start, X), OL3),
    findall(X, has(X, Start), OL4),
    findall(X, able_to(Start, X), OL5),
    findall(X, able_to(X, Start), OL6),
    findall(X, used_to(Start, X), OL7),
    findall(X, used_to(X, Start), OL8).

(4) I don't like cuts (!) so maybe I'm wrong but... why do you put ! as first element in is_a/2 ?

is_a(X, H) :- !, f_is_a(X, H).
is_a(X, H) :- !, \+f_is_a(X, P), H = X.
is_a(X, H) :- !, is_a(X, P), is_a(P, H).

If I'm not wrong, the cut in first clause (!, f_is_a(X, H)) disable the second and the third clause so, if f_is_a(X, H) fail, the second and the third clauses are never verified.

You're sure that your intention wasn't

is_a(X, H) :- f_is_a(X, H), !.
is_a(X, H) :- \+f_is_a(X, P), H = X, !.
is_a(X, H) :- is_a(X, P), is_a(P, H), !.

or better

is_a(X, H) :- f_is_a(X, H), !.
is_a(X, X) :- \+f_is_a(X, _), !.
is_a(X, H) :- is_a(X, P), is_a(P, H), !.

?

Or not cut at all?

(5) same cut problem with has/3; I suspect that

has(X, H) :- !, f_has(X, H).
has(X, H) :- !, \+f_has(X, P), H = X.
has(X, H) :- !, has(X, P), has(P, H).

is wrong and that your intention was

has(X, H) :- f_has(X, H), !.
has(X, H) :- \+f_has(X, P), H = X, !.
has(X, H) :- has(X, P), has(P, H), !.

or better

has(X, H) :- f_has(X, H), !.
has(X, X) :- \+f_has(X, _), !.
has(X, H) :- has(X, P), has(P, H), !.

Or not cut at all?

max66
  • 65,235
  • 10
  • 71
  • 111