views:

469

answers:

2

Hello, I decided to study some logic programming and I stumbled across a problem. It is programmed in SWI Prolog.

test(A, B, N):-
 nonvar(B),
 B = final,
 true.

test(A, B, N):-
 N > 2,
 test(A, final, N).

test(A, B, N):-
 N1 is N + 1,
 test(N1, B, N1).

It is just a sample with no real use except it is driving me crazy.

The problem is that when the code reaches true then it starts tracking back and answers "true". But I need to "return" value A. How do I do that?

+2  A: 

I don't have my prolog compiler here but have you tried something along the lines of:

test(A, B, N, A):-
 nonvar(B),
 B = final,
 true.

test(A, B, N, Result):-
 N > 2,
 test(A, final, N, Result).

test(A, B, N, Result):-
 N1 is N + 1,
 test(N1, B, N1, Result).
ruibm
I need to return a value of variable A which is 3 when true is called. How do I achieve that? Your code above doesn't do that.Thanks,Petr
petr
How about like this then? (I'll make this code work in 2h time when I'm back home)
ruibm
The code above seems to run fine, I just tried it. Can you give me an example of a sample input/output?
ruibm
It looks that your solution works. I have to go now so I'll test it tomorrow. But I'm confused. Why does Prolog returns true when there is only a single A and why does it return the value when there are two As in the first predicate?
petr
It just seems to me that the variable Result is redundant.
petr
It's not redundant. It basically only gets unified/assigned when you get to the end of the recursion so you can retrieve its value at the top level call.
ruibm
+4  A: 

A is not being Unified with anything in the body of your rules. The way prolog works is via unification of terms. You cannot "return" A as in procedural languages as such. For instance, what do you want the value of A to be when the recursion comes to an end? I have no idea what your code is doing so let me use an example of my own.

  accumulate([], A, A).
  accumulate([H|T], A, N) :- A1 is A + H, accumulate(T, A1, N).

  sum([], 0).
  sum(L, N) :- accumulate(L,0,N).

Here is a sum procedure that will sum the values in a list and "return N", the sum of the values in the list. To call this procedure you can do this:

  sum([2, 3, 4], N).

And Prolog will respond:

  N = 9

Notice the accumulate procedure is using A as an accumulator as the recursion goes on. That is, A keeps the running sum, while N is the final answer it returns. During the recursion N is not unified with any real value.

In the final step of the recursion, that is, when the list is empty, the value of A is unified with N, in effect returning N.


Let us do a Trace.

 [trace] 4 ?- test(A, B, 0).
   Call: (7) test(_G417, _G418, 0) ? creep//A unifies with _G417 (internal variable name), B with _G418 and N with 0.
   Call: (8) nonvar(_G418) ? creep
   Fail: (8) nonvar(_G418) ? creep
   Redo: (7) test(_G417, _G418, 0) ? creep//Unifies with clause 2, 
^  Call: (8) 0>2 ? creep
^  Fail: (8) 0>2 ? creep
   Redo: (7) test(_G417, _G418, 0) ? creep //Unifies with clause 3
^  Call: (8) _L183 is 0+1 ? creep
^  Exit: (8) 1 is 0+1 ? creep
   Call: (8) test(1, _G418, 1) ? creep //recursive call, unifies with 
   Call: (9) nonvar(_G418) ? creep
   Fail: (9) nonvar(_G418) ? creep
   Redo: (8) test(1, _G418, 1) ? creep
^  Call: (9) 1>2 ? creep
^  Fail: (9) 1>2 ? creep
   Redo: (8) test(1, _G418, 1) ? creep
^  Call: (9) _L195 is 1+1 ? creep
^  Exit: (9) 2 is 1+1 ? creep
   Call: (9) test(2, _G418, 2) ? creep
   Call: (10) nonvar(_G418) ? creep
   Fail: (10) nonvar(_G418) ? creep
   Redo: (9) test(2, _G418, 2) ? creep
^  Call: (10) 2>2 ? creep
^  Fail: (10) 2>2 ? creep
   Redo: (9) test(2, _G418, 2) ? creep
^  Call: (10) _L207 is 2+1 ? creep
^  Exit: (10) 3 is 2+1 ? creep
   Call: (10) test(3, _G418, 3) ? creep
   Call: (11) nonvar(_G418) ? creep
   Fail: (11) nonvar(_G418) ? creep
   Redo: (10) test(3, _G418, 3) ? creep
^  Call: (11) 3>2 ? creep
^  Exit: (11) 3>2 ? creep
   Call: (11) test(3, final, 3) ? creep
   Call: (12) nonvar(final) ? creep
   Exit: (12) nonvar(final) ? creep
   Call: (12) final=final ? creep
   Exit: (12) final=final ? creep
   Call: (12) true ? creep
   Exit: (12) true ? creep
   Exit: (11) test(3, final, 3) ? creep
   Exit: (10) test(3, _G418, 3) ? creep
   Exit: (9) test(2, _G418, 2) ? creep
   Exit: (8) test(1, _G418, 1) ? creep
   Exit: (7) test(_G417, _G418, 0) ? creep

Now, notice the point in the trace where I marked //A unifies with _G417 (internal variable name), B with _G418 and N with 0.. At that point A is your external variable and _G417 is your internal A. If this call succeeds which it ultimately does prolog will only report the external variable values. Internally _G417 is never unified with anything else. I think the problem is one of understanding how the unification model of Prolog works.

Vincent Ramdhanie
I believe that my problem is a little bit different.Look at my sample:I ask Prolog test(A, B, 0).Prolog goes though predicates and counts N1 until N1 is > 2. When this happen predicate calls recursively test(A, final, N). So at this step A is 3, N is 3 and B is final. nonvar(B) succeeds, B = final succeeds so true is called. At this step Prolog stars backtracking and in the end returns true. But I asked about A and B, not true. I need the value A returned by Prolog.
petr
I don't know how to force the predicate to terminate at the point where value A is assigned. I tried cuts but no success.
petr
@petr See my edit above.
Vincent Ramdhanie
Vincent is right. For a less noisy debug, try test(A, B, N):- write(triple(A,B,N)), N1 is N + 1, test(N1, B, N1). as the last clause.
Charles Stewart