Often, when solving real problems, you must pursue a path to its logical conclusion. If this conclusion does not give the answer you were looking for, you must choose an alternate path. For instance, you might have played maze games when you were a child. One sure way to find the end of the maze was to turn left at every fork in the maze until you hit a dead end. At that point you would back up to the last fork, and try the right-hand path, once again turning left at each branch encountered. By methodically trying each alternate path, you would eventually find the right path and win the game.
Visual Prolog uses this same backing-up-and-trying-again method, called backtracking, to find a solution to a given problem. As Visual Prolog begins to look for a solution to a problem (or goal), it might have to decide between two possible cases. It sets a marker at the branching spot (known as a backtracking point) and selects the first subgoal to pursue. If that subgoal fails (equivalent to reaching a dead end), Visual Prolog will backtrack to the back-tracking point and try an alternate subgoal.
Here is a simple example:
/* Program ch04e02.pro */
/* 현실 문제를 풀때 논리적인 결론에 이르는 path를 찾아야 한다. 결론이 찾는 답을 주지 못할 때 다른 path를 선택하게 된다.prolog는 주어진 goal에 만족하는 모든 답을 찾아야 한다. 이때 goal을 만족시킬 경우는 다른 답을 찾기 위해, 만족시키지 못할 경우는 답을 찾기위해 database를 처음부터 탐색해 나간다. 이를 backtracking이라하는 backing-up-and-trying-again method 이다. goal에 대한 하나의 solution을 찾기 시작하면서 branching spot ( = backtracking point )을 marker로서 표시하고 first subgoal이 fail 하면 그 point로 되돌아가 다른 subgoal을 try 한다 */
PREDICATES
nondeterm likes(symbol,symbol)
tastes(symbol,symbol)
nondeterm food(symbol)
CLAUSES
likes(bill,X):- /* What 은 variable
X에 unified , rule 의 head가
matching됨*/
food(X),
/* rule의 body에서 첫번째 subgoal 호출(call)
*/
tastes(X,good).
/* tastes(brussels_sprouts,good)를 찾기위해 top으로부터 탐색. call이 fail하고 자동적으로 backtracking 수행된다 */
tastes(pizza,good).
/* 첫번째 subgoal 의 만족여부를 위해 top에서부터 search */
tastes(brussels_sprouts,bad).
food(brussels_sprouts).
/* call된 fact 다음에 backtracking point를 둔다, X 는 brussels_sprouts
로 bind */
food(pizza).
/* 한번 bind된 변수는 backtracking에 의해서만 free 하게 된다. X는 pizza에 bind 되고 tastes(pizza,good) 가 새로이 호출 (call) 된다. 다시 top으로부터 탐색이 되고 match가 이루어져 goal은 성공적으로 리턴된다. likes rule에서 What은 X와 unify되고 X는 값 pizza와 bind 되었기 때문에 What 은 값 pizza 와 새로이 bind 되어 답을 낸다 */
GOAL
likes(bill, What).
출력
What = pizza
1 solution
/* rule의 head부 match를 위한 search는 program의 top에서부터 시작한다
새로운 call이 있을 때 그에 match되는 것도 top에서부터 search한다
하나의 call이 성공적으로 match 되면 다른 subgoal을 차례로 수행한다
clause에서 한번 bind된 변수는 backtracking에 의해서만 free 하게 된다 */
This small program is made up of two sets of facts and one rule. The rule, represented by the relationship likes, simply states that Bill likes good-tasting food.
To see how backtracking works, give the program the following goal to solve:
likes(bill, What).
|
When Prolog begins an attempt to satisfy a goal, it starts at the top of the program in search of a match. |
In this case, it will begin the search for a solution by looking from the top for a match to the subgoal likes(bill, What).
It finds a match with the first clause in the program, and the variable What is unified with the variable X. Matching with the head of the rule causes Visual Prolog to attempt to satisfy that rule. In doing so, it moves on to the body of the rule, and calls the first subgoal located there: food(X).
When a new call is made, a search for a match to that call also begins at the top of the program. |
In the search to satisfy the first subgoal, Visual Prolog starts at the top, attempting a match with each fact or head of a rule encountered as processing goes down into the program.
It finds a match with the call at the first fact representing the food relationship. Here, the variable X is bound to the value brussels_sprouts. Since there is more than one possible answer to the call food(X), Visual Prolog sets a backtracking point next to the fact food(brussels_sprouts). This backtracking point keeps track of where Prolog will start searching for the next possible match for food(X).
When a call has found a successful match, the call is said to succeed, and the next subgoal in turn may be tried. |
With X bound to brussels_sprouts, the next call made is
tastes(brussels_sprouts, good)
and Visual Prolog begins a search to attempt to satisfy this call, again starting from the top of the program. Since no clause is found to match, the call fails and Visual Prolog kicks in its automatic backtracking mechanism. When backtracking begins, Prolog retreats to the last backtracking point set. In this case, Prolog returns to the fact food(brussels_sprouts).
Once a variable has been bound in a clause, the only way to free that binding is through backtracking. |
When Prolog retreats to a backtracking point, it frees all the variables set after that point, and sets out to find another solution to the original call.
The call was food(X), so the binding of brussels_sprouts for X is released. Prolog now tries to resolve this call, beginning from the place where it left off. It finds a match with the fact food(pizza)] and returns, this time with the variable X bound to the value pizza.
Prolog now moves on to the next subgoal in the rule, with the new variable binding. A new call is made, tastes(pizza, good)], and the search begins at the top of the program. This time, a match is found and the goal returns successfully.
Since the variable What in the goal is unified with the variable X in the likes rule, and the variable X is bound to the value pizza, the variable What is now bound to the value pizza and Visual Prolog reports the solution
What=pizza
1 Solution
As we've described earlier, with the aid of backtracking, Visual Prolog will not only find the first solution to a problem, but is actually capable of finding all possible solutions.
Consider Program 3, which contains facts about the names and ages of some players in a racquet club.
/* Program ch04e03.pro */
/* prolog는 문제에 대한 하나의 solution만을 찾는 것이 아니고 가능한 모든 solution들을 전부 찾는다. 그것이 backtracking 의 잇점이다.*/
DOMAINS
child = symbol
age =
integer
PREDICATES
nondeterm player(child,
age)
CLAUSES
player(peter,9).
player(paul,10).
player(chris,9).
player(susan,9).
GOAL
/* compound goal */
player(Person1,
9),
player(Person2,
9),
Person1
<> Person2.
/* 나이가 9살이면서 다른 사람인 Person1 과 Person2을 찾아라 */
출력
Person1 = peter, Person2 = chris
Person1 = peter, Person2 = susan
Person1 = chris, Person2 = peter
Person1 = chris, Person2 = susan
Person1 = susan, Person2 = peter
Person1 = susan, Person2 = chris
6 solutions
You'll use Visual Prolog to arrange a ping-pong tournament between the nine-year-olds in a racquet club. There will be two games for each pair of club players. Your aim is to find all possible pairs of club players who are nine years old. This can be achieved with the compound goal:
player(Person1, 9),
player(Person2, 9),
Person1 <> Person2.
In natural language: Find Person1 (age 9) and Person2 (age 9) so that Person1 is different from Person2.
Visual Prolog will try to find a solution to the first subgoal player(Person1, 9) and continue to the next subgoal only after the first subgoal is reached. The first subgoal is satisfied by matching Person1 with peter. Now Visual Prolog can attempt to satisfy the next subgoal:
player(Person2, 9)
by also matching Person2 with peter. Now Prolog comes to the third and final subgoal
Person1 <> Person2
Since Person1 and Person2 are both bound to peter, this subgoal fails. Because of this, Visual Prolog backtracks to the previous subgoal, and searches for another solution to the second subgoal:
player(Person2, 9)
This subgoal is fulfilled by matching Person2 with chris.
Now, the third subgoal:
Person1 <> Person2
can succeed, since peter and chris are different. Here, the entire goal is satisfied by creating a tournament between the two players, chris and peter.
However, since Visual Prolog must find all possible solutions to a goal, it backtracks to the previous goal--hoping to succeed again. Since
player(Person2, 9)
can also be satisfied by taking Person2 to be susan, Visual Prolog tries the third subgoal once again. It succeeds (since peter and susan are different), so another solution to the entire goal has been found.
Searching for more solutions, Visual Prolog once again backtracks to the second subgoal, but all possibilities for this subgoal have been exhausted. Because of this, backtracking now continues back to the first subgoal. This can be satisfied again by matching Person1 with chris. The second subgoal now succeeds by matching Person2 with peter, so the third subgoal is satisfied, again fulfilling the entire goal. Here, another tournament has been scheduled, this time between chris and peter.
Searching for yet another solution to the goal, Visual Prolog backtracks to the second subgoal in the rule. Here, Person2 is matched to chris and again the third subgoal is tried with these bindings. The third subgoal fails, since Person1 and Person2 are equal, so backtracking regresses to the second subgoal in search of another solution. Person2 is now matched with susan, and the third subgoal succeeds, providing another tournament for the racket club (chris vs. susan).
Once again, searching for all solutions, Prolog backtracks to the second subgoal, but this time without success. When the second subgoal fails, backtracking goes back to the first subgoal, this time finding a match for Person1 with susan. In an attempt to fulfill the second subgoal, Prolog matches Person2 with peter, and subsequently the third subgoal succeeds with these bindings. A fifth tournament has been scheduled for the players.
Backtracking again goes to the second subgoal, where Person2 is matched with chris. A sixth solution is found for the racquet club, producing a full set of tournaments.
The final solution tried is with both Person1 and Person2 bound to susan. Since this causes the final subgoal to fail, Visual Prolog must backtrack to the second subgoal, but there are no new possibilities. Visual Prolog then backtracks to the first subgoal, but the possibilities for Person1 have also been exhausted. No more solutions can be found for the goal, so the program terminates.
Type in this compound goal for the program:
player(Person1, 9),
player(Person2, 9),
Person1 <> Person2.
Verify that Visual Prolog responds with
Person1=peter, Person2=chris
Person1=peter, Person2=susan
Person1=chris, Person2=peter
Person1=chris, Person2=susan
Person1=susan, Person2=peter
Person1=susan, Person2=chris
6 Solutions
Notice how backtracking might cause Visual Prolog to come up with redundant solutions. In this example, Visual Prolog does not distinguish that Person1 = peter is the same thing as Person2 = peter. We will show you later in this chapter how to control the search Visual Prolog generates.
Exercise in Backtracking
Using Program 3, decide what Visual Prolog will reply to the following goal:
player(Person1, 9), player(Person2, 10).
Check your answer by typing in the exercise and the given goal when you run the program.
A Detailed Look at Backtracking
With this simple example under your belt, you can take a more detailed look at how Visual Prolog's backtracking mechanism works. Start by looking at Program 4 in light of the following goal, which consists of two subgoals:
likes(X, wine) , likes(X, books)
When evaluating the goal, Visual Prolog notes which subgoals have been satisfied and which have not. This search can be represented by a goal tree:

Before the goal evaluation begins, the goal tree consists of two unsatisfied subgoals. In the following goal tree diagrams, a subgoal satisfied in the goal tree is marked with an underline, and the corresponding clause is shown beneath that subgoal.
/* Program ch04e04.pro */
backtracking 의 4가지 기본원칙
1. subgoal은 위에서 아래로 순서대로 만족되어야 한다
2. Predicate clause는 프로그램에 나타나는 순서대로 위에서 아래로 test되어야 한다
3. subgoal이 rule의 head(결론) 과 match되면 다음에 rule의 body(전제)가 만족되어야 한다. 그때 rule의 body가 만족되는 subgoal의 새로운 집합을 구성한다. 이것은 goal tree를 만든다
4. goal tree의 extremities (leaves)각자에 대해 matching fact가 발견되면 비로소 goal이 만족되는 것이다
DOMAINS
name,thing = symbol
PREDICATES
likes(name, thing)
reads(name)
is_inquisitive(name)
CLAUSES
likes(john,wine):-!. /* subgoal likes(X,wine)
와 match, X는 john과 bind*/
likes(lance,skiing):-!.
likes(lance,books):-!.
likes(lance,films):-!.
likes(Z,books):- reads(Z), is_inquisitive(Z).
/* likes(X,books)와 match,변수 Z는 X와 match 가능, unify . rule의 body를 만족시켜야...새로운 subgoal집합으로 tree구성*/
reads(john). /* 새로운 subgoal tree를 만족시킴 */
is_inquisitive(john). /* extremity 각자에 대한 matching fact */
GOAL
likes(X,wine), likes(X,books)
/* 2개의 subgoal로 구성되는 goal tree */
출력
X = john
1 solution
In this example, the goal tree shows that two subgoals must be satisfied. To do so, Visual Prolog follows the first basic principle of backtracking:
Subgoals must be satisfied in order, from top to bottom. |
Visual Prolog determines which subgoal it will use when trying to satisfy the clause according to the second basic principle of backtracking:
|
Predicate clauses are tested in the order they appear in the program, from top to bottom. |
When executing Program 4, Visual Prolog finds a matching clause with the first fact defining the likes predicate. Take a look at the goal tree now.

The subgoal likes(X, wine) matches the fact likes(john, wine) and binds X to the value john. Visual Prolog tries to satisfy the next subgoal to the right.
The call to the second subgoal begins a completely new search with the binding X = john. The first clause
likes(john, wine)
does not match the subgoal
likes(X, books)
since wine is not the same as books. Visual Prolog must therefore try the next clause, but lance does not match the value X (because, in this case, X is bound to john), so the search continues to the third clause defining the predicate likes:
likes(Z, books):- reads(Z), is_inquisitive(Z).
The argument Z is a variable, so it is able to match with X. The second arguments agree, so the call matches the head of the rule. When X matches Z, the arguments are unified. With the arguments unified, Visual Prolog will equate the value X has (which is john) with the variable Z. Because of this, now the variable Z also has the value john.
The subgoal now matches the left side (head) of a rule. Continued searching is determined by the third basic principle of backtracking:
When a subgoal matches the head of a rule, the body of that rule must be satisfied next. The body of the rule then constitutes a new set of subgoals to be satisfied. |
This yields the following goal tree:

The goal tree now includes the subgoals
reads(Z) and is_inquisitive(Z)
where Z is bound to the value john. Visual Prolog will now search for facts that match both subgoals. This is the resulting final goal tree:

According to the fourth basic principle of backtracking:
A goal has been satisfied when a matching fact is found for each of the extremities (leaves) of the goal tree. |
So now the initial goal is satisfied.
Visual Prolog uses the result of the search procedure in different ways, depending on how the search was initiated. If the goal is a call from a subgoal in the body of a rule, Visual Prolog attempts to satisfy the next subgoal in the rule after the call has returned. If the goal is a query from the user, Visual Prolog replies directly:
X=john
1 Solution
As you saw in Program 4, having once satisfied an external goal, Visual Prolog backtracks to find all alternate solutions. It also backtracks if a subgoal fails, hoping to re-satisfy a previous subgoal in such a way that the failed subgoal is satisfied by other clauses.
To fulfill a subgoal, Visual Prolog begins a search with the first clause that defines the predicate. One of two things can then happen:
It finds a matching clause, in which case the following occurs:
If there is another clause that can possibly re-satisfy the subgoal, Visual Prolog places a pointer (to indicate a backtracking point) next to the matching clause.
All free variables in the subgoal that match values in the clause are bound to the corresponding values.
If the matching clause is the head of a rule, that rule's body is then evaluated; the body's subgoals must succeed for the call to succeed.
It can't find a matching clause, so the goal fails. Visual Prolog backtracks as it attempts to re-satisfy a previous subgoal. When processing reaches the last backtracking point, Visual Prolog frees all variables that had been assigned new values since the backtracking point was set, then attempts to re-satisfy the original call.
Visual Prolog begins a search from the top of the program. When it backtracks to a call, the new search begins from the last backtracking point set. If the search is unsuccessful, it backtracks again. If backtracking exhausts all clauses for all subgoals, the goal fails.
Backtracking
Here is another, slightly more complex, example, illustrating how backtracking takes place in Prolog.
/* Program ch04e05.pro */
PREDICATES
nondeterm type(symbol,
symbol)
nondeterm
is_a(symbol, symbol)
lives(symbol,
symbol)
nondeterm
can_swim(symbol)
CLAUSES
type(ungulate,animal). /* type(X,animal) 과 match, X는
ungulate에 bind */
type(fish,animal). /*
backtracking point, redo : X는 fish에 bind */
is_a(zebra,ungulate).
/* is_a(Y,X) 와 match, Y는 zebra와 bind */
is_a(herring,fish).
/* redo : is_a(Y,ungulate) 와 no match (fail),
Y는 herring에 bind*/
is_a(shark,fish).
/* " */ /* backtracking point, redo : Y는 shark에 bind */
lives(zebra,on_land).
lives(frog,on_land).
lives(frog,in_water).
lives(shark,in_water).
can_swim(Y):-
/*
head of rule match */
type(X,animal),
/* call */
is_a(Y,X),
/* is_a(Y,ungulate)를 call, is_a(Y,fish)를 call */
lives(Y,in_water).
/* lives(zebre,in_water)를 call , no match (fail),
lives(herring,in_water)를 call, no match,
lives(shark,in_water)를
call, match */
GOAL
can_swim(What),
write("A ",What,"
can swim\n").
출력
A shark can swim
What = shark
1 solution
This example program uses an internal goal to illustrate how backtracking works. When the program is compiled and run, Visual Prolog will automatically begin executing the goal, attempting to satisfy all the subgoals in the goal section.
Visual Prolog calls the can_swim predicate with a free variable, What. In trying to solve this call, Visual Prolog searches the program looking for a match. It finds a match with the clause defining can_swim, and the variable What is unified with the variable Y.
Next, Visual Prolog attempts to satisfy the body of the rule. In doing so, Visual Prolog calls the first subgoal in the body of the rule, type(X, animal), and searches for a match to this call. It finds a match with the first fact defining the type relationship.
At this point, X is bound to ungulate. Since there is more than one possible solution, Visual Prolog sets a backtracking point at the fact type(ungulate, animal).
With X bound to ungulate, Visual Prolog makes a call to the second subgoal in the rule (is_a(Y, ungulate)), and again searches for a match. It finds one with the first fact, is_a(zebra, ungulate). Y is bound to zebra and Prolog sets a backtracking point at is_a(zebra, ungulate).
Now, with X bound to ungulate and Y bound to zebra, Prolog tries to satisfy the last subgoal, lives(zebra, in_water). Prolog tries each lives clause, but there is no lives(zebra, in_water) clause in the program, so the call fails and Prolog begins to backtrack in search of another solution.
When Visual Prolog backtracks, processing returns to the last point where a backtracking point was placed. In this case, the last backtracking point was placed at the second subgoal in the rule, on the fact is_a(zebra, ungulate).
When Visual Prolog reaches a backtracking point, it frees the variables that were assigned new values after the last backtracking point and attempts to find another solution to the call it made at that time. In this case, the call was is_a(Y, ungulate).
Visual Prolog continues down into the clauses in search of another clause that will match with this one, starting from the point where it previously left off. Since there are no other clauses in the program that can match this one, the call fails and Visual Prolog backtracks again in an attempt to solve the original goal.
From this position, the last backtracking point was set at type(ungulate, animal).
Visual Prolog frees the variables set in the original call and tries to find another solution to the call type(X, animal). The search begins after the backtracking point. Visual Prolog finds a match with the next type fact in the program (type(fish, animal)); X is bound to fish, and a new backtracking point is set at that fact.
Visual Prolog now moves down to the next subgoal in the rule; since this is a new call, the search begins at the top of the program with is_a(Y, fish).
Visual Prolog finds a match to this call and Y is bound to herring.
Since Y is now bound to herring, the next subgoal called is lives(herring, in_water). Again, this is a new call, and the search begins from the top of the program.
Visual Prolog tries each lives fact, but fails to find a match and the subgoal fails.
Visual Prolog now returns to the last backtracking point, is_a(herring, fish).
The variables that were bound by this matching are now freed. Starting at the point where it last left off, Visual Prolog now searches for a new solution to the call is_a(Y, fish).
Visual Prolog finds a match with the next is_a clause, and Y becomes bound to the symbol shark.
Visual Prolog tries the last subgoal again, with the variable Y bound to shark. It calls lives(shark, in_water); the search begins at the top of the program, since this is a new call. It finds a match and the last subgoal to the rule succeeds.
At this point, the body of the can_swim(Y) rule is satisfied. Visual Prolog returns Y to the call can_swim(What). Since What is bound to Y, and Y is bound to shark, What is now bound to shark in the goal.
Visual Prolog continues processing where it left off in the goal section, and calls the second subgoal in the goal.
Visual Prolog completes the program by outputting
A shark can swim.
and the program terminates successfully.

Figure 4.1: How the can_swim Program Works
Prolog's built-in backtracking mechanism can result in unnecessary searching; because of this, inefficiencies can arise. For instance, there may be times when you want to find unique solutions to a given question. In other cases, it may be necessary to force Visual Prolog to continue looking for additional solutions even though a particular goal has been satisfied. In cases such as these, you must control the backtracking process. In this section, we'll show you some techniques you can use to control Visual Prolog's search for the solutions to your goals.
Visual Prolog provides two tools that allow you to control the backtracking mechanism: the fail predicate, which is used to force backtracking, and the cut (signified by !), which is used to prevent backtracking.
Visual Prolog begins backtracking when a call fails. In certain situations, it's necessary to force backtracking in order to find alternate solutions. Visual Prolog provides a special predicate, fail, to force failure and thereby encourage backtracking. The effect of the fail predicate corresponds to the effect of the comparison 2 = 3 or any other impossible subgoal. Program 6 illustrates the use of this special predicate.
/* Program ch04e06.pro */
backtracking mechanism은 불필요한 탐색을 하여 비능률을 초래할수 있다. 즉 단 하나의 (unique)답만을 찾는 경우 라든가 반대로 여러개의 답을 찾기위해 추가적인 탐색을 강요하는 경우가 있다. 이를 위해서 backtracking process를 제어할 필요가 있다. 이를 위해서는 2가지 방법을 사용한다.
1. backtracking을 강요 (force)하는 fail predicate.
2. backtracking을 방지 (prevent)하는 cut ( ! ).
하나의 call이 실패하면 backtracking하게 되는데 어떤 경우에는 부가적인 답을 찾기위해서 backtracking을 강제할 경우가 있다. fail 문장은 failure를 강제하고 그럼으로써 backtracking을 야기한다. fail 문장의 효과는 2 = 3 과 같은 impossible subgoal 의 효과와 같다. 다음은 fail 문장의 예이다.
DOMAINS
name = symbol
PREDICATES
nondeterm father(name,
name)
everybody
CLAUSES
father(leonard,katherine).
father(carl,jason).
father(carl,marilyn).
everybody:- /* father(X,Y) 의 또다른 답을 위해
backtracking */
father(X,Y),
write(X," is
",Y,"'s father\n"),
fail.
/* 결코 만족될수
없는 항상 fail, backtracking 강요*/
everybody. /*
fail 이 없다면 backtrack할 이유가 없다 */
/* fail 은 last call까지 backtrack 한다 */
GOAL
everybody. /* father(X,
Y) 보다도 더 명확한 답을 보여준다*/
/* prolog에서 여러개의 답을 낼수 있도록 마지막 call까지 backtrack하게되는데 이러한 call을 non-deterministic call 이라 한다. 단지 하나의 답만을 낼수 있는 call을 deterministic call이라 한다*/
출력
leonard is katherine's father /*fail에 의해서 가능한 모든 답을 낸다*/
carl is jason's father
carl is marilyn's father
yes
Once an internal goal has completely succeeded, there is nothing that tells Visual Prolog to backtrack. Because of this, an internal call to father will come up with only one solution. However, the predicate everybody in Program 6 uses fail to force backtracking, and therefore finds all possible solutions.
The object of the predicate everybody is to produce a cleaner response from program runs. Compare the answers to the two preceding goals:
Goal father(X, Y).
X=leonard, Y=katherine
X=carl, Y=jason
X=carl, Y=marilyn
3 Solutions
and
Goal everybody.
leonard is katherine's father
carl is jason's father
carl is marilyn's father
yes
The predicate everybody uses backtracking to generate more solutions for father(X, Y) by forcing Prolog to backtrack through the body of the everybody rule:
father(X, Y), write(X," is ",Y,"'s father\n"), fail.
fail can never be satisfied (it always fails), so Visual Prolog is forced to backtrack. When backtracking takes place, Prolog backtracks to the last call that can produce multiple solutions. Such a call is labeled non-deterministic. A non-deterministic call contrasts with a call that can produce only one solution, which is a deterministic call.
The write predicate can't be re-satisfied (it can't offer new solutions), so Visual Prolog must backtrack again, this time to the first subgoal in the rule.
Notice that it's useless to place a subgoal after fail in the body of a rule. Since the predicate fail always fails, there would be no way of reaching a subgoal located after fail.
Exercises
Load and run Program 6 and evaluate the following goals:
a. father(X, Y).
b. everybody.
2. Edit the body of the rule defining everybody so that the rule ends with the call to the write predicate (delete the call to fail). Now compile and run the program, giving everybody. as the goal. Why doesn't Visual Prolog find all the solutions as it does with the query father(X, Y)?
3. Relocate the call to fail at the end of the everybody rule. Again, give the query everybody as the goal. Why are the solutions to everybody terminated by no? For a clue, append everybody. as a second clause to the definition of predicate everybody and re-evaluate the goal.
Visual Prolog contains the cut, which is used to prevent backtracking; it's written as an exclamation mark (!). The effect of the cut is simple: It is impossible to backtrack across a cut.
You place the cut in your program the same way you place a subgoal in the body of a rule. When processing comes across the cut, the call to cut immediately succeeds, and the next subgoal (if there is one) is called. Once a cut has been passed, it is not possible to backtrack to subgoals placed before the cut in the clause being processed, and it is not possible to backtrack to other predicates defining the predicate currently in process (the predicate containing the cut).
There are two main uses of the cut:
When you know in advance that certain possibilities will never give rise to meaningful solutions, it's a waste of time and storage space to look for alternate solutions. If you use a cut in this situation, your resulting program will run quicker and use less memory. This is called a green cut.
When the logic of a program demands the cut, to prevent consideration of alternate subgoals. This is a red cut.
How to Use the Cut
In this section, we give examples that show how you can use the cut in your programs. In these examples, we use several schematic Visual Prolog rules (r1, r2, and r3), which all describe the same predicate r, plus several subgoals (a, b, c, etc.).
Prevent Backtracking to a Previous Subgoal in a Rule
r1 :- a, b, !, c.
This is a way of telling Visual Prolog that you are satisfied with the first solution it finds to the subgoals a and b. Although Visual Prolog is able to find multiple solutions to the call to c through backtracking, it is not allowed to backtrack across the cut to find an alternate solution to the calls a or b. It is also not allowed to backtrack to another clause that defines the predicate r1.
As a concrete example, consider Program 7.
/* Program ch04e07.pro */
backtracking을 막기위해서 exclamation mark (!)를 사용하는 것을 cut 이라 한다. cut을 가로질러 backtrack하는 것은 불가능하다. rule의 body에 subgoal을 위치시키는 것과 같은 방식으로 cut을 위치시킨다. cut 으로의 호출은 즉시 성공하고 다음 subgoal이 호출된다. 일단 cut 이 지나가면 cut 이전의 subgoal로 backtrack하는 것은 불가능하다. 또한 cut을 포함한 predicate를 정의하고 있는 다른 predicate로의 backtrack도 불가능하다. cut 의 용도는 크게 2가지이다.
1. 의미있는 답이 나올 가능성이 전혀 없다는 것을 미리 알고 있을 때 다른 답을 구하는 것은 시간과 memory의 낭비이다. 이 상황에서 cut을 사용하면 프로그램은 더 빨리 실행되고 memory를 덜 사용한다. 이것을 green cut 이라 부른다
2. 프로그램의 logic이 cut을 요구할 때, 즉 추가의 subgoal 의 고려를 방지하기 위해서 사용한다. 이것을 red cut 이라 한다
r1 :- a, b, !, c.
위의 문장에서 subgoal a, b 에 대한 답이 구해진후에 c 에 대한 답이 backtrack에 의해 여러개 구해지더라도 call a, b 에 대한 추가적인 해를 구하기 위한 backtrack는 허용되지 않는다. 또한 predicate r1을 정의한 또다른 clause로의 backtrack도 허용되지 않는다.
PREDICATES
buy_car(symbol,symbol)
nondeterm car(symbol,symbol,integer)
colors(symbol,symbol)
CLAUSES
buy_car(Model,Color):-
car(Model,Color,Price),
/*first subgoal*/
colors(Color,sexy),!,
/*cut을 만나는 순간 bind되었던 변수값들 동결*/
Price > 25000.
/*
test succeed*/
car(maserati,green,25000).
car(corvette,black,24000). /* match,Color
는 black로 bind */
car(corvette,red,26000).
/*backtrack, match, Color는 red로
bind,
Price는 26000*/
car(porsche,red,24000).
colors(red,sexy).
/* sexy 가 메치됨*/
colors(black,mean). /* 선택된
차가 sexy color인지 테스트, fail*/
colors(green,preppy).
GOAL
buy_car(corvette,
Y).
/* 위에서 Price < 25000 이라면 test 는 fail, 앞쪽의 subgoal 로 backtrack 할수 없어 fjnal subgoal 인 Price 문제의 해결을 위한 방법은 없고 goal 은 failure 로 끝난다 */
출력
Y = red
1 solution
In this example, the goal is to find a Corvette with a sexy color and a price that's ostensibly affordable. The cut in the buy_car rule means that, since there is only one Corvette with a sexy color in the database, if its price is too high there's no need to search for another car.
Given the goal
buy_car(corvette, Y)
Visual Prolog calls car, the first subgoal to the buy_car predicate.
It makes a test on the first car, the Maserati, which fails.
It then tests the next car clauses and finds a match, binding the variable Color with the value black.
It proceeds to the next call and tests to see whether the car chosen has a sexy color. Black is not a sexy color in the program, so the test fails.
Visual Prolog backtracks to the call to car and once again looks for a Corvette to meet the criteria.
It finds a match and again tests the color. This time the color is sexy, and Visual Prolog proceeds to the next subgoal in the rule: the cut. The cut immediately succeeds and effectively "freezes into place" the variable bindings previously made in this clause.
Visual Prolog now proceeds to the next (and final) subgoal in the rule: the comparison
Price < 25000.
This test fails, and Visual Prolog attempts to backtrack in order to find another car to test. Since the cut prevents backtracking, there is no other way to solve the final subgoal, and the goal terminates in failure.
Prevent Backtracking to the Next Clause
The cut can be used as a way to tell Visual Prolog that it has chosen the correct clause for a particular predicate. For example, consider the following code:
r(1):- ! , a , b , c.
r(2):- ! , d.
r(3):- ! , c.
r(_):- write("This is a catchall
clause.").
Using the cut makes the predicate r deterministic. Here, Visual Prolog calls r with a single integer argument. Assume that the call is r(1). Visual Prolog searches the program, looking for a match to the call; it finds one with the first clause defining r. Since there is more than one possible solution to the call, Visual Prolog places a backtracking point next to this clause.
Now the rule fires and Visual Prolog begins to process the body of the rule. The first thing that happens is that it passes the cut; doing so eliminates the possibility of backtracking to another r clause. This eliminates backtracking points, increasing the run-time efficiency. It also ensures that the error-trapping clause is executed only if none of the other conditions match the call to r.
Note that this type of structure is much like a "case" structure written in other programming languages. Also notice that the test condition is coded into the head of the rules. You could just as easily write the clauses like this:
r(X) :- X = 1 , ! , a , b , c.
r(X) :- X = 2 , ! , d.
r(X) :- X = 3 , ! , c.
r(_) :- write("This is a catchall
clause.").
However, you should place the testing condition in the head of the rule as much as possible, as doing this adds efficiency to the program and makes for easier reading.
As another example, consider the following program. Run this program and give the query friend(bill, Who) as the goal.
/* Program ch04e08.pro */
r(1) :- !, a, b, c.
r(2) :- !, d.
r(3) :- !, c.
r(_) :- write ("This is a catchall clause.").
cut을 사용하여 predicate r을 deterministic하게 만든다. r(1), r(2), r(3), r(_) 중에서 하나라도 body of rule이 성공하면 cut은 backtracking point를 제거하여 또다른 r clause로의 backtracking 가능성을 없애서 단 하나의 r만 수행되게 한다. 다른 r 의 어떤 condition도 match가 되지 않을 때 마지막의 error message 문장이 수행된다
PREDICATES
friend(symbol,symbol)
girl(symbol)
likes(symbol,symbol)
CLAUSES
friend(bill,jane):-
girl(jane),
likes(bill,jane),!.
friend(bill,jim):-
likes(jim,baseball),!.
/* cut이 없다면 답은 2개 */
friend(bill,sue):-
girl(sue).
girl(mary).
girl(jane).
girl(sue).
likes(jim,baseball).
likes(bill,sue).
GOAL
friend(bill,Who).
출력
Who = jim
1 solution
/* non-deterministic clause를 body of rule 에 cut을 삽입함으로써 deterministic clause로 만들수 있다. 위에서 friend predicate는 하나를 리턴하여 오직 하나의 solution만을 가지므로 deterministic 하다.*/
Without cuts in the program, Visual Prolog would come up with two solutions: Bill is a friend of both Jane and Sue. However, the cut in the first clause defining friend tells Visual Prolog that, if this clause is satisfied, it has found a friend of Bill and there's no need to continue searching for more friends. A cut of this type says, in effect, that you are satisfied with the solution found and that there is no reason to continue searching for another friend.
Backtracking can take place inside the clauses, in an attempt to satisfy the call, but once a solution is found, Visual Prolog passes a cut. The friend clauses, written as such, will return one and only one friend of Bill's (given that a friend can be found).
Determinism and the Cut
If the friend predicate (defined in the previous program) were coded without the cuts, it would be a non-deterministic predicate (one capable of generating multiple solutions through backtracking). In many implementations of Prolog, programmers must take special care with non-deterministic clauses because of the attendant demands made on memory resources at run time. However, Visual Prolog makes internal checks for non-deterministic clauses, reducing the burden on you, the programmer.
However, for debugging (and other) purposes, it can still be necessary for you to intercede; the check_determ compiler directive is provided for this reason. If check_determ is inserted at the very beginning of a program, Visual Prolog will display a warning if it encounters any non-deterministic clauses during compilation.
You can make non-deterministic clauses into deterministic clauses by inserting cuts into the body of the rules defining the predicate. For example, placing cuts in the clauses defining the friend predicate causes that predicate to be deterministic because, with the cuts in place, a call to friend can return one, and only one, solution.
The not Predicate
This program demonstrates how you can use the not predicate to identify an honor student: one whose grade point average (GPA) is at least 3.5 and who is not on probation.
/* Program ch04e10.pro */
not 은 subgoal이 true라고 증명될수 없을 때 성공한다
DOMAINS
name = symbol
gpa = real
PREDICATES
nondeterm honor_student(name)
nondeterm student(name,
gpa)
probation(name)
CLAUSES
honor_student(Name):-
student(Name,
GPA),
GPA>=3.5,
not(probation(Name)).
student("Betty
Blue", 3.5).
student("David Smith",
2.0).
student("John Johnson", 3.7).
probation("Betty
Blue").
probation("David Smith").
GOAL
honor_student(X).
출력
X = John Johnson
1 solution
/* probation : 보호 관찰중인 상태 */
There is one thing to note when using not: The not predicate succeeds when the subgoal can't be proven true. This results in a situation that prevents unbound variables from being bound within a not. When a subgoal with free variables is called from within not, Visual Prolog will return the error message Free variables not allowed in 'not' or 'retractall'. This happens because, for Prolog to bind the free variables in a subgoal, that subgoal must unify with some other clause and the subgoal must succeed. The correct way to handle unbound variables within a not subgoal is with anonymous variables.
Here are some examples of correct clauses and incorrect clauses.
likes(bill, Anyone):- /* 'Anyone' is an output argument
*/
likes(sue, Anyone),
not(hates(bill, Anyone).
In this example, Anyone is bound by likes(sue, Anyone) before Visual Prolog finds out that hates(bill, Anyone) is not true. This clause works just as it should.
If you rewrite this so that it calls not first, you will get an error message to the effect that free variables are not allowed in not.
likes(bill, Anyone):- /*
This won't work right */
not(hates(bill, Anyone)),
likes(sue, Anyone).
Even if you correct this (by replacing Anyone in not(hates(bill, Anyone)) with an anonymous variable) so that the clause does not return the error, it will still return the wrong result.
likes(bill, Anyone):- /*
This won't work right */
not(hates(bill, _)),
likes(sue, Anyone).
This clause states that Bill likes Anyone if nothing that Bill hates is known and if Sue likes Anyone. The original clause stated that Bill likes Anyone if there is some Anyone that Sue likes and that Bill does not hate.
Example
Always be sure that you think twice when using the not predicate. Incorrect use will result in an error message or errors in your program's logic. The following is an example of the proper way to use the not predicate.
/* Program ch04e11.pro */
not 은 부적절하게 사용시 error message를 내거나 logic상의 error를 발생시킨다. 다음은 not 의 적절한 사용예이다.
PREDICATES
nondeterm likes_shopping(symbol)
nondeterm has_credit_card(symbol,symbol)
bottomed_out(symbol,symbol)
CLAUSES
likes_shopping(Who):-
has_credit_card(Who,Card),
not(bottomed_out(Who,Card)),
write(Who,"
can shop with the ",Card, " credit card.\n").
has_credit_card(chris,visa).
has_credit_card(chris,diners).
has_credit_card(joe,shell).
has_credit_card(sam,mastercard).
has_credit_card(sam,citibank).
bottomed_out(chris,diners).
bottomed_out(sam,mastercard).
bottomed_out(chris,visa).
GOAL
likes_shopping(Who).
출력
joe can shop with the shell credit card.
Who = joe
sam can shop with the citibank credit card.
Who = sam
2 solutions
Exercises
Suppose an average taxpayer in the USA is a married US citizen with two children who earns no less than $500 a month and no more than $2,000 per month. Define a special_taxpayer predicate that, given the goal special_taxpayer(fred)., will succeed only if fred fails one of the conditions for an average taxpayer. Use the cut to ensure that there is no unnecessary backtracking.
Players in a certain squash club are divided into three leagues, and players may only challenge members in their own league or the league below (if there is one).
Write a Visual Prolog program that will display all possible matches between club players in the form:
tom versus bill
marjory versus annette
Use the cut to ensure, for example, that
tom versus bill
and
bill versus tom
are not both displayed.
This is an exercise in backtracking, not a test of your ability to solve murder mysteries. Load and run the following program, then enter the goal killer(X).
(Note: Bert is guilty because he has a motive and is smeared in the same stuff as the victim.)
/* Program ch04e12.pro */
다음은 backtracking 에 관한 연습이다.
Bert 는 motive(동기)를 가지고 있고 희생자와 같은 stuff (음식 또는 오물)로 smeared_in (더럽혀졌기) 때문에 유죄다.
trace
DOMAINS
name,sex,occupation,object,vice,substance
= symbol
age=integer
PREDICATES
nondeterm person(name,
age, sex, occupation)
nondeterm
had_affair(name, name)
killed_with(name,
object)
killed(name)
nondeterm killer(name)
motive(vice)
smeared_in(name,
substance)
owns(name,
object)
nondeterm
operates_identically(object, object)
nondeterm
owns_probably(name, object)
nondeterm
suspect(name)
/* *
* 살해 사건과 관련된 사실 * * */
CLAUSES
person(bert,55,m,carpenter).
person(allan,25,m,football_player).
person(allan,25,m,butcher).
person(john,25,m,pickpocket).
had_affair(barbara,john).
had_affair(barbara,bert).
had_affair(susan,john).
killed_with(susan,club).
killed(susan).
motive(money).
motive(jealousy).
motive(righteousness).
smeared_in(bert,
blood).
smeared_in(susan, blood).
smeared_in(allan,
mud).
smeared_in(john, chocolate).
smeared_in(barbara,chocolate).
owns(bert,wooden_leg).
owns(john,pistol).
/* * * 배경 지식 * * */
operates_identically(wooden_leg,
club).
operates_identically(bar, club).
operates_identically(pair_of_scissors,
knife).
operates_identically(football_boot, club).
owns_probably(X,football_boot):-
person(X,_,_,football_player).
owns_probably(X,pair_of_scissors):-
person(X,_,_,hairdresser).
owns_probably(X,Object):-
owns(X,Object).
/* *
* * * * * * * * * * * * * * * * * * * * *
* Susan 이 살해될
만한 무기를 가진 사람을 의심 *
* * * * * * * * * * * * * * * * * * * * * * */
suspect(X):-
killed_with(susan,Weapon)
,
operates_identically(Object,Weapon)
,
owns_probably(X,Object).
/* *
* * * * * * * * * * * * * * * * * * * * * * * *
* Susan
과 관계를 가진 남자를 의심 *
* * * *
* * * * * * * * * * * * * * * * * * * * * */
suspect(X):-
motive(jealousy),
person(X,_,m,_),
had_affair(susan,X).
/* *
* ** *
* * *
* * *
* * *
* * * * * * * * * * * * * * * *
* Susan을 아는 어떤
사람과 관계를 가진 여성을 의심 *
* * * * *
* * * * * * * * * * ** *
* * *
* * *
* * * * * * */
suspect(X):-
motive(jealousy),
person(X,_,f,_),
had_affair(X,Man),
had_affair(susan,Man).
/* *
* * * * * * * * * * * * * * * * * * * * * * * * *
* 돈을
노린 소매치기를 의심 *
* * *
* * * * * * * * * * * * * * * * * * * * * * * */
suspect(X):-
motive(money),
person(X,_,_,pickpocket).
killer(Killer):-
person(Killer,_,_,_),
killed(Killed),
Killed <>
Killer, /* It is not a suicide */
suspect(Killer),
smeared_in(Killer,Goo),
smeared_in(Killed,Goo).
GOAL
killer(X).
출력
X = bert
1 solution