Lessons/Succeed, fail and backtrack - part 3

From wiki.visual-prolog.com

Here we will consider more ways to use the knowledge in our little "knowledge base" (i.e. from Succeed, fail and backtrack - part 2).

Exercise Update the runGoal clauses like this:
clauses
    runGoal() :-
        father(Person, Father),
        stdio::writef("% is the father of %.\n", Father, Person).
And run the program.

This clause will call father with two free variables, i.e. both arguements are output arguments. Therefore it will succeed for the first clause and have created a backtrack point to the second clause. And so forth.

Notice that the actual backtracking takes place from run to father.

Exercise Try this instead:
clauses
    runGoal() :-
        foreach father(Person, Father) do
            stdio::writef("% is the father of %.\n", Father, Person)
        end foreach,
        stdio::write("That is all we know about fathers\n").

foreach <P> do <Q> end foreach is a construction that execute <P> and if it succeeds then it executes <Q> after <Q> has executed it will backtrack into <P> if that is possible and if <P> succeeds again then <Q> is also run again, this continues until <P> finally fails, at which point the excecution will continue after end foreach.

fail is a built-in predicate that always fails, it can be used to achieve a siilar behavior as the one foreach achieves:

Exercise Try this:
clauses
    runGoal() :-
        father(Person, Father),
        stdio::writef("% is the father of %.\n", Father, Person),
        fail.
    runGoal() :-
        stdio::write("That is all we know about fathers\n").

After we have found a Person and its Father and written it we call fail. And since fail fails we will backtrack:

  • Into father if there is a backtrack point there.
  • And otherwise to the second runGoal clause.

So besides the syntactic difference in using foreach and fail there is also the difference that after having found all fathers, the foreach construction will succeed and execution will therefore continue after end foreach in the same clause; fail on the other hand fails, so in after having found all fathers fail will fail, and we will therefore the entire clause containing fail will itself fail, and execution will therefore continue in the next clause.

Summary:

  • We can use the "knowledge base" with different combinations of bound and free arguments.
  • foreach can be used to exhaust backtracking of a certain "generator" and perform some action of each "generated thing".
  • when the backtracking in a foreach construction is finally exhausted, the entire foreach construction succeeds.
  • fail is a built-in predicate that fails when called.
Exercise Try this:
clauses
    runGoal() :-
        Person = "Sue",
        if father(Person, Father) then
            stdio::writef("% is the father of %.\n", Father, Person)
        else
            stdio::writef("We don't kno who the father of % is.\n", Person)
        end if,
        stdio::write("<<end>>\n").
Also try it with Person = "Pam".

if <C> then <P> else <Q> end if is a construction that will first execute <C> if that succeeds it will execute <P> if it fails it will execute <Q> instead. Backtrack points in <C> (if any) will be discarded, so there will be no backtracking in to <C>. So <C> is used to choose between <P> and <Q>. If (lets us say) <P> is chosen then the entire if construction functions as if <P> was called. So if <P> fails then the entire if construction fails, and it <P> succeds then the entire if construction succeeds. Finally, if <P> created backtrack points then backtracking will return into <P>.

If you don't want anything to happen in the then-part you can use the built-in succeed predicate, which simply succeeds, or you can write nothing at all:

if <C> then succeed else <Q> end if
if <C> then else <Q> end if

If you don't want anything to take place in the else part you can also leave out the word else, so these three things have the same meaning:

if <C> then <P> else succeed end if
if <C> then <P> else end if
if <C> then <P> end if

Summary

  • succeed is a build-in predicate that succeeds
  • if <C> then <P> else <Q> end if runs C to choose between <P> and <Q>.