0% found this document useful (0 votes)
38 views

Counting in Prolog: CS 538 Spring 2006

Lesson30

Uploaded by

Mihaela Grubii
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
38 views

Counting in Prolog: CS 538 Spring 2006

Lesson30

Uploaded by

Mihaela Grubii
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 22

Counting in Prolog

Rules that involve counting often use


the is predicate to evaluate a
numeric value.
Consider the relation len(L,N) that
is true if the length of list L is N.
len([],0).
len([_|T],N) :-
len(T,M), N is M+1.
| ?- len([1,2,3],X).
X = 3
| ?- len(Y,2).
Y = [_10903,_10905]
The symbols _10903 and _10905 are
“internal variables” created as needed
when a particular value is not forced
in a solution.

©
CS 538 Spring 2006 447
Debugging Prolog
Care is required in developing and
testing Prolog programs because the
language is untyped; undeclared
predicates or relations are simply
treated as false.
Thus in a definition like
adj([A,B|_]) :- A=B.
adj([_,B|T]) :- adk([B|T]).
| ?- adj([1,2,2]).
no
(Some Prolog systems warn when an
undefined relation is referenced, but
many others don’t).

©
CS 538 Spring 2006 448
Similarly, given
member(A,[A|_]).
member(A,[_|T]) :-
member(A,[T]).
| ?- member(2,[1,2]).
Infinite recursion! (Why?)

If you’re not sure what is going on,


Prolog’s trace feature is very handy.
The command
trace.
turns on tracing. (notrace turns
tracing off).
Hence
| ?- trace.
yes
[trace]
| ?- member(2,[1,2]).

©
CS 538 Spring 2006 449
(1) 0 Call: member(2,[1,2]) ?
(1) 1 Head [1->2]:
member(2,[1,2]) ?
(1) 1 Head [2]:
member(2,[1,2]) ?
(2) 1 Call: member(2,[[2]]) ?
(2) 2 Head [1->2]:
member(2,[[2]]) ?
(2) 2 Head [2]:
member(2,[[2]]) ?
(3) 2 Call: member(2,[[]]) ?
(3) 3 Head [1->2]:
member(2,[[]]) ?
(3) 3 Head [2]: member(2,[[]])
?
(4) 3 Call: member(2,[[]]) ?
(4) 4 Head [1->2]:
member(2,[[]]) ?
(4) 4 Head [2]: member(2,[[]])
?
(5) 4 Call: member(2,[[]]) ?

©
CS 538 Spring 2006 450
Termination Issues in Prolog
Searching infinite domains (like
integers) can lead to non-
termination, with Prolog trying every
value.
Consider
odd(1).
odd(N) :- odd(M), N is M+2.
| ?- odd(X).
X = 1 ;
X = 3 ;
X = 5 ;
X = 7

©
CS 538 Spring 2006 451
A query
| ?- odd(X), X=2.
going into an infinite search,
generating each and every odd
integer and finding none is equal to
2!
The obvious alternative,
odd(2) (which is equivalent to
X=2, odd(X)) also does an infinite,
but fruitless search.
We’ll soon learn that Prolog does
have a mechanism to “cut off”
fruitless searches.

©
CS 538 Spring 2006 452
Definition Order can Matter
Ideally, the order of definition of
facts and rules should not matter.
But,
in practice definition order can
matter. A good general guideline is to
define facts before rules. To see why,
consider a very complete database of
motherOf relations that goes back as
far as
motherOf(cain,eve).
Now we define
isMortal(X) :-
isMortal(Y), motherOf(X,Y).
isMortal(eve).

©
CS 538 Spring 2006 453
These definitions state that the first
woman was mortal, and all
individuals descended from her are
also mortal.
But when we try as trivial a query as
| ?- isMortal(eve).
we go into an infinite search!
Why?
Let’s trace what Prolog does when it
sees
| ?- isMortal(eve).
It matches with the first definition
involving isMortal, which is
isMortal(X) :-
isMortal(Y), motherOf(X,Y).
It sets X=eve and tries to solve
isMortal(Y), motherOf(eve,Y).
It will then expand isMortal(Y) into

©
CS 538 Spring 2006 454
isMortal(Z), motherOf(Y,Z).
An infinite expansion ensues.
The solution is simple—place the
“base case” fact that terminates
recursion first.
If we use
isMortal(eve).
isMortal(X) :-
isMortal(Y), motherOf(X,Y).
yes
| ?- isMortal(eve).
yes
But now another problem appears!
If we ask
| ?- isMortal(clarkKent).
we go into another infinite search!
Why?

©
CS 538 Spring 2006 455
The problem is that Clark Kent is from
the planet Krypton, and hence won’t
appear in our motherOf database.
Let’s trace the query.
It doesn’t match isMortal(eve).
We next try
isMortal(clarkKent) :-
isMortal(Y),
motherOf(clarkKent,Y).
We try Y=eve, but eve isn’t Clark’s
mother. So we recurse, getting:
isMortal(Z), motherOf(Y,Z),
motherOf(clarkKent,Y).
But eve isn’t Clark’s grandmother
either! So we keep going further back,
trying to find a chain of descendents
that leads from eve to clarkKent.
No such chain exists, and there is no

©
CS 538 Spring 2006 456
limit to how long a chain Prolog will
try.
There is a solution though!
We simply rewrite our recursive
definition to be
isMortal(X) :-
motherOf(X,Y),isMortal(Y).
This is logically the same, but now we
work from the individual X back
toward eve, rather than from eve
toward X. Since we have no
motherOf relation involving
clarkKent, we immediately stop our
search and answer no!

©
CS 538 Spring 2006 457
Extra-logical Aspects of
Prolog
To make a Prolog program more
efficient, or to represent negative
information, Prolog needs features
that have a procedural flavor. These
constructs are called “extra-logical”
because they go beyond Prolog’s core
of logic-based inference.

©
CS 538 Spring 2006 458
The Cut
The most commonly used extra-
logical feature of Prolog is the “cut
symbol,” “!”
A ! in a goal, fact or rule “cuts off”
backtracking.
In particular, once a ! is reached (and
automatically matched), we may not
backtrack across it. The rule we’ve
selected and the bindings we’ve
already selected are “locked in” or
“frozen.”
For example, given
x(A) :- y(A,B), z(B), ! , v(B,C).
once the ! is hit we can’t backtrack
to resatisfy y(A,B) or z(B) in some
other way. We are locked into this

©
CS 538 Spring 2006 459
rule, with the bindings of A and B
already in place.
We can backtrack to try various
solutions to v(B,C).
It is sometimes useful to have several
!’s in a rule. This allows us to find a
partial solution, lock it in, find a
further solution, then lock it in, etc.
For example, in a rule
a(X) - b(X), !, c(X,Y), ! , d(Y).
we first try to satisfy b(X), perhaps
trying several facts or rules that
define the b relation. Once we have a
solution to b(X), we lock it in, along
with the binding for X.
Then we try to satisfy c(X,Y), using
the fixed binding for X, but perhaps
trying several bindings for Y until
c(X,Y) is satisfied.

©
CS 538 Spring 2006 460
We then lock in this match using
another !.
Finally we check if d(Y) can be
satisfied with the binding of Y already
selected and locked in.

©
CS 538 Spring 2006 461
When are Cuts Needed?
A cut can be useful in improving
efficiency, by forcing Prolog to avoid
useless or redundant searches.
Consider a query like
member(X,list1),
member(X,list2), isPrime(X).
This asks Prolog to find an X that is in
list1 and also in list2 and also is
prime.
X will be bound, in sequence, to each
value in list1. We then check if X is
also in list2, and then check if X is
prime.
Assume we find X=8 is in list1 and
list2. isPrime(8) fails (of course).
We backtrack to member(X,list2)
and try to resatisfy it with the same
value of X.

©
CS 538 Spring 2006 462
But clearly there is never any point in
trying to resatisfy member(X,list2).
Once we know a value of X is in
list2, we test it using isPrime(X).
If it fails, we want to go right back to
member(X,list1) and get a
different X.
To create a version of member that
never backtracks once it has been
satisfied we can use !.
We define
member1(X,[X|_]) :- !.
member1(X,[_|Y]) :-
member1(X,Y).
Our query is now
member(X,list1),
member1(X,list2), isPrime(X).
(Why isn’t member1 used in both
terms?)

©
CS 538 Spring 2006 463
Expressing Negative
Information
Sometimes it is useful to state rules
about what can’t be true. This allows
us to avoid long and fruitless
searches.
fail is a goal that always fails. It can
be used to represent goals or results
that can never be true.
Assume we want to optimize our
grandMotherOf rules by stating that
a male can never be anyone’s
grandmother (and hence a complete
search of all motherOf and
fatherOf relations is useless).
A rule to do this is
grandMotherOf(X,GM) :-
male(GM), fail.

©
CS 538 Spring 2006 464
This rule doesn’t do quite what we
hope it will!
Why?
The standard approach in Prolog is to
try other rules if the current rule fails.
Hence we need some way to “cut off”
any further backtracking once this
negative rule is found to be
applicable.
This can be done using
grandMotherOf(X,GM) :-
male(GM),!, fail.

©
CS 538 Spring 2006 465
Other Extra-Logical
Operators
• assert and retract
These operators allow a Prolog
program to add new rules during
execution and (perhaps) later remove
them. This allows programs to learn
as they execute.
•findall

Called as findall(X,goal,List)
where X is a variable in goal. All
possible solutions for X that satisfy
goal are found and placed in List.
For example,
findall(X,
(append(_,[X|_],[-1,2,-3,4]),(X<0)),
L).
L = [-1,-3]

©
CS 538 Spring 2006 466
• var and nonvar
var(X) tests whether X is unbound
(free).
nonvar(Y) tests whether Y is bound
(no longer free).
These two operators are useful in
tailoring rules to particular
combinations of bound and unbound
variables.
For example, the rule
grandMotherOf(X,GM) :-
male(GM),!, fail.
might backfire if GM is not yet bound.
We could set GM to a person for
whom male(GM) is true, then fail
because we don’t want grandmothers
who are male!

©
CS 538 Spring 2006 467
To remedy this problem. we use the
rule only when GM is bound. Our rule
becomes
grandMotherOf(X,GM) :-
nonvar(GM), male(GM),!, fail.

©
CS 538 Spring 2006 468

You might also like