Programming Languages (CSC-302 98S)
Outline of Class 34: More Prolog Programming
Held: Monday, April 27, 1998
- A few people waited until today (or possibly tomorrow) to take the
exam. This means that I probably won't be able to get them back
until late this week or possibly early next week. I will have an
answer key ready on Wednesday.
- Assignment six, on
Prolog, is now ready. Hopefully, you will find it shorter than the
- A makeup assignment for
assignment five is
now ready. Those of you who turned the assignment in late are
encouraged to do the makeup, even though you may not know your
grade until after the makeup is due.
- Recall that Prolog uses the following syntax for lists: lists are
surrounded by brackets; items in a list are separated by
commas; the vertical bar acts like cons (more or less).
 -- the empty list
[a,b,c] -- a list of three elements
[a|X] -- a list whose first element is a and whose
remainder is the list X.
- [a,b|X] -- a list whose first two elements are a and b and
whose remainder is the list X.
- We can, of course, use Prolog to do things like "real computaton", such
as manipulating lists.
- In fact, we can even use patterns to do it.
- The difficulty is that we have to do everything as a predicate.
head. Traditionally, we think of head as
removing the first element of a list. However, in Prolog, we need
to think of it as determining whether something is at the head of
the list (and then use to proof techniques to extract the head of
- Here's one definition
head(X,L) :- equal(L,[Y|YS]), equal(X,Y).
- You might read this as "X is the head of L if L's first element is Y
and X is equal to Y."
- However, this is somewhat inelegant. It is much nicer to use some
head(X,[Y|Ys]) :- equal(X,Y).
- You might read this as "X is the head of the list beginning with Y if
X equals Y."
- In fact, we can go even further.
- You might read this as "X is the head of the list that begins with X".
- Note that these are more sophisticated than the patterns of Haskell.
In particular, we're able to reuse variables
- We can define tail similarly.
- Let's design a trinary
concatenate(Xs,Ys,Zs) holds when
Zs is the
list created by concatenating
- When you concatenate the empty list with anything, you get that thing.
- When you concatenate the list beginning with X with anything, you
get a list beginning with X and whose remainder is Whatever if
when you concatenate the rest of the first list with the second
list, you get Whatever.
concat([X|Xs], Ys, [X|Whatever]) :-
- Note that we don't need a rule that says "when you concatenate
anything with the empty list, you get the empty list". Why not?
Because that's already been handled by the first two cases and is
therefore redundant (and redundancy can be much more dangerous in
Prolog than it is in other languages).
- We can use this predicate in many ways.
- We can ask whether the concatenation of two lists is a third list.
concat([a,b,c], [d,e,f], [a,b,c,d,e,f]).
- We can ask what list we create by concatenating two lists.
concat([a,b,c], [d,e,f], [a,b,c,d,e,f]).
- We might be able to ask what list we need to concatenate to
one list to get another.
concat([a,b,c], X, [a,b,c,d,e,f]).
concat(X, [d,e,f], [a,b,c,d,e,f]).
- Can we go so far as to ask what lists we need to concatenate to
get a third? Try it and see.
concat(Xs, Ys, [a,b,c,d,e,f]).
- Now we're ready to think about more sophisiticated predicates, like
- When is X and member of the list L?
- Do we have to worry about explaining when X is not a member? No,
because Prolog works under the assumption that "if I can't prove it,
then it's not true"
- Note that a well-designed
member might even be able
to extract the members of a list.
- Prolog lets you define functions that, it many ways, resemble
Haskell's constructors (but without the sophisticated typing).
- Basically, you can use a function within a predicate and Prolog does a
form of "pattern matching" on that function.
- For example, we might use
empty to define trees.
in_tree(Val,empty) :- fail.
Val < NodeVal, in_tree(Val,Less).
Val > NodeVal, in_tree(Val,Greater).
- Think about how you'd write
- While Prolog generally uses the same philosophy as the functional languages
(you type an expression and see a result) it also includes a more traditional
mechanism for output (similar to scheme's
write(X) predicate always holds and has the side effect
of writing its argument to stdout.
nl predicate always holds and has the side effect
of writing a newline to stdout.
- For example, to extend our
in_tree function to list the nodes
it looks at, we might write:
showpath(Val,empty) :- write("not found!"), nl, fail.
Val < NodeVal, write(NodeVal), nl, showpath(Val,Less).
Val > NodeVal, write(NodeVal), nl, showpath(Val,Greater).
- Let's return to an old question: how might we implement a langauge like
Prolog? That is, how do we use predicate definitions to answer questions?
- One basic strategy:
Keep track of collections of intermediate "goals"
(predicates whose truth/falsity must be determined)
Pick a goal
(usually the first in the collection)
Find a rule that might help solve that goal
(usually the first rule whose lhs matches the goal)
Determine replacements necessary to match the rule to the goal
(variables to be replaced by atoms)
Replace the goal in the set with the corresponding preconditions
(the stuff to the right of :-, after the substition is made).
If you run out of goals, the predicate holds.
If you can't solve a goal, "backtrack" to try another rule.
- You can draw this as a search tree. Normal prolog implementations
traverse this tree in a left-to-right depth-first search.
- Note that when matching a goal that includes variables with
a definition that uses constants, we instatiate the variables
to the constants.
- Similarly, when the goal includes constants and the rule includes
variables, instatiate the variables in the definition with the
constants before replacing with the corresponding right hand side.
- Prolog stops when it finds the first proof (and set of instatiations).
- In most implementations, you can make it continue by typing a semicolon.
- There are also techniques for automatically finding all results.