Search code examples
prologlogicbacktrackingunificationredo

Prolog redoes a call and fails for no apparent reason


I'm fairly new to Prolog. Anyway, I'm trying to write a recursive set of rules that return the average number of characters per word in a given list of character codes. My code is below.

medellangd(Text,AvgLen) :-
    medellangd(Text,T,1,0,0),
    AvgLen = T.

medellangd([],AvgLen,Space,Words,Chars) :-
    T is (Chars/Words),
    AvgLen = T.
medellangd([A|B],AvgLen,Space,Words,Chars) :-
    succ(Chars,C),
    updatewords(A,Space,Words,W),
    updatespace(A,S),
    medellangd(B,T,S,W,C),
    AvgLen = T.

updatewords(A,1,Words,W) :-
    member(A, [65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122])
    -> succ(Words,S),
       W = S
    ;  W = Words.
updatewords(A,0,Words,W) :-
        W = Words.

updatespace(A,S) :-
    member(A,[65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122])
    -> S = 0
    ;  S = 1.

For reasons which I cannot tell, although AvgLen gets the right value, Prolog returns false when I call medellangd([68,69],AvgLen). When I trace this call, although every call initially is exited before AvgLen gets its value, Prolog decides to redo "(9) updatewords(68, 1, 0, _G2574)" if I enter a semicolon after the AvgLen value assignment, and fails. Why does this happen?


Solution

  • You predicate is working fine, in order to find a solution prolog tries every possible way, so after giving the answer AvgLen=2 it sercheas for more possible solutions. When Prolog is trying to find a solution it builds a proof tree, where it keeps all the possible ways to prove the goal and try them all one by one until it finds all the right answers and no other ways to prove the goal had left. This is the reason that calls redo, to try more possible solutions. If you want the predicate to be deterministic you could add the cut(!) in:

    medellangd(Text,AvgLen) :-
        medellangd(Text,T,1,0,0),
        AvgLen = T,!.
    

    and these will stop when finds the first right answer and will not search further.

    A simple example is to understand how prolog works is:

    simple_example([]).
    simple_example([_]).
    

    The above predicate succeeds if you query simple_example(L). where L is empty or it has one element. Now if you try to query simple_example([]). or simple_example([1]). in trace you will see :

    [trace]  ?- simple_example([1]).
       Call: (7) simple_example([1]) ? creep
       Exit: (7) simple_example([1]) ? creep
    true.
    

    On the other hand if you write the same example differently:

      simple_example2(L):- L=[].
      simple_example2(L):- L=[_].
    

    The predicate simple_example2 is clearly equivalent to simple_example but if you query simple_example2([]). in trace you will see that because we have [] matches both L in simple_example2 it will try both and of course only first will be right:

    [trace]  ?- simple_example2([1]).
       Call: (7) simple_example2([1]) ? Unknown option (h for help)
       Call: (7) simple_example2([1]) ? Unknown option (h for help)
       Call: (7) simple_example2([1]) ? Unknown option (h for help)
       Call: (7) simple_example2([1]) ? creep
       Call: (8) [1]=[] ? creep
       Fail: (8) [1]=[] ? creep
       Redo: (7) simple_example2([1]) ? creep
       Call: (8) [1]=[_G3328] ? creep
       Exit: (8) [1]=[1] ? creep
       Exit: (7) simple_example2([1]) ? creep
    true.