1
votes

I am learning Prolog and I have a small question. I'm working on an example which "builds" a tower.

So I define 3 blocks a, b and c. The third block c lays on top of a and b -> so it is supported by a and b.

block(a).
block(b).
block(c).

%supported(BLOCK, BY1, BY2)
supported(c,a,b).


level(BLOCK, LEVEL) :-
      supported(BLOCK, X, _)
   -> (level(X, LEV1), LEVEL is LEV1 + 1)
   ;  LEVEL is 0.

I also have a function to calculate the level of a block. The problem I have is the following:

?- level(X, 0).
false.

?- level(X, 1).
X = c.

Why is it returning false for the level 0 and how can i fix it? I would like to have a method which returns me a and b for level 0.

2

2 Answers

1
votes

One can debug this using trace. (added indentation):

[trace]  ?- level(X, 0).
   Call: (6) level(_G2697, 0) ? creep
     Call: (7) supported(_G2697, _G2771, _G2772) ? creep
     Exit: (7) supported(c, a, b) ? creep
     Call: (7) level(a, _G2771) ? creep
       Call: (8) supported(a, _G2771, _G2772) ? creep
       Fail: (8) supported(a, _G2771, _G2772) ? creep
     Redo: (7) level(a, _G2771) ? creep
       Call: (8) _G2770 is 0 ? creep
       Exit: (8) 0 is 0 ? creep
     Exit: (7) level(a, 0) ? creep
     Call: (7) 0 is 0+1 ? creep
     Fail: (7) 0 is 0+1 ? creep
   Fail: (6) level(_G2697, 0) ? creep
   false.

So what happened?

First you call level(X,0). This predicate calls supported with supported(BLOCK,X,_). There is one answer that unifies: BLOCK=a, X=b and _=c. So that means the if-then-else statement uses the then-part.

In the then-part, it queries level/2 again with level(b,LEV1), now this call results in a call to support(b,LEV1,_). For this call, it can't resolve the call, because there is no support predicate with the first value b. So now we take the else-part. We unify LEV1 is 0, thus LEV1=0 and we return.

In the return part (in the then-part), level(b,0) is the result. Now we unify LEVEL is LEV1+1, LEVEL however was already grounded as 0 and 0+1 is 1. Thus it fails on the top level.

The resolution is probably to make supported finer, and define it as: support/2.

The program reads:

block(a).
block(b).
block(c).

%supported(BLOCK, BY1, BY2)
supported(c,b).
supported(c,a).
supported(b,a).

Now the level predicate reads:

level(B,L) :-
    supported(B,C),
    level(C,LC),
    L is LC+1.
level(_,0).
1
votes

consider that

?- level(a,0).
true.

it's evident that when BLOCK is free the supported(BLOCK, X, _) succeeds, forcing the rule on the unintended conjuntion level(X, LEV1), LEVEL is LEV1 + 1, that indeed fails.

To correct the behaviour, bind BLOCK:

level(BLOCK, LEVEL) :-
  block(BLOCK),
  (supported(BLOCK, X, _) -> level(X, LEV1), LEVEL is LEV1 + 1 ; LEVEL is 0).