J.Olufsen
J.Olufsen

Reputation: 13915

Cannot increment variable N by 1 using is/2 predicate

Cannot increment variable N by 1 using is/2 predicate. N is always 0 in the repeat loop. Why? How to increment it?

:- dynamic audible/1, flashes/1,tuned/1.

audible(false).
flashes(false).
tuned(false).

turn :-
  N is 0 ,
  repeat ,
  (
    incr(N,N1)    ,
    N1 =:= 5000   ,
    audible(true) ,
    flashes(true) -> retractall(tuned) ,
    retractall(flashes) ,
    retractall(audible) ,
    assert( tuned(true)   ) ,
    assert( flashes(true) ) ,
    assert( audible(true) )
  ) .

incr(X,X1) :- X1 is X+1 .

Upvotes: 0

Views: 1528

Answers (2)

Nicholas Carey
Nicholas Carey

Reputation: 74277

I haven't a clue what you're trying to accomplish here, but it would appear that you're trying to write procedural prolog code.

It doesn't work.

Prolog variables are write-once: having been unified with an object they cease to be variable. They become that object, until that unification is undone via backtracking.

You need to learn to think recursively and to use recursion loops to do what you want.

Your turn/0 predicate

turn :-
  N is 0 ,
  repeat ,
  (
    incr(N,N1)    ,
    N1 =:= 5000   ,
    audible(true) ,
    flashes(true) -> retractall(tuned) ,
    retractall(flashes) ,
    retractall(audible) ,
    assert( tuned(true)   ) ,
    assert( flashes(true) ) ,
    assert( audible(true) )
  ) .

does the following. It:

  • unifies N with 0,
  • increments N1 with N+1, making N1 = 1
  • fails if N1 is something other than 5000 (which it is).
  • On failure, the prolog engine starts backtracking...
  • backtracking through incr/2 undoes N1's binding, making it variable again.
  • backtracking into repeat/0, succeeds again.

At this point, you're back to where you started, with N bound to 0.

Wash, rinse, repeat.

Upvotes: 1

Daniel Lyons
Daniel Lyons

Reputation: 22803

Because N is 0. Variables in Prolog are not assignables. Here's what happens if you trace your loop:

?- trace, turn.
Call: (7) turn ? 
Call: (8) _G492 is 0 ? 
Exit: (8) 0 is 0 ? 
Call: (8) repeat ? 
Exit: (8) repeat ? 
Call: (8) incr(0, _G493) ? 
Call: (9) _G495 is 0+1 ? 
Exit: (9) 1 is 0+1 ? 
Exit: (8) incr(0, 1) ? 
Call: (8) 1=:=5000 ? 
Fail: (8) 1=:=5000 ? 
Redo: (8) repeat ? 
Exit: (8) repeat ? 
Call: (8) incr(0, _G493) ? 
Call: (9) _G495 is 0+1 ? 
Exit: (9) 1 is 0+1 ? 
Exit: (8) incr(0, 1) ? 
Call: (8) 1=:=5000 ? 
Fail: (8) 1=:=5000 ? 
Redo: (8) repeat ? 
Exit: (8) repeat ? 
Call: (8) incr(0, _G493) ? 
Call: (9) _G495 is 0+1 ? 
Exit: (9) 1 is 0+1 ? 
Exit: (8) incr(0, 1) ? 
Call: (8) 1=:=5000 ? 
Fail: (8) 1=:=5000 ? 
Redo: (8) repeat ? 

See what's happening there? You're repeatedly asking if 1 is 5000, which fails, and then you try again. But the value of N and N1 will never change within the body of a predicate. Prolog does not have block-scoped variables like you're accustomed to. You need to make the body of the loop into a separate predicate and use recursion to accomplish what you're trying to do. It's going to look something like this:

turn :- loop(0).
loop(5000) :-
  audible(true), ...
loop(N) :-
  incr(N, N1),
  loop(N1).

By the way, there's already a predicate succ/2 that does what you're trying to do with incr/2.

Upvotes: 3

Related Questions