Programming Languages (CSC-302 98S)

[Instructions] [Search] [Current] [Changes] [Syllabus] [Handouts] [Outlines] [Assignments]

# 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 previous assignments.
• 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.

## Working with Lists

• 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.
• Consider `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 the list).
• 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 patterns.
```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.
```head(X,[X|XS]).
```

• 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.
```tail(XS,[X|XS]).
```

• Let's design a trinary `concatenate`. `concatenate(Xs,Ys,Zs)` holds when `Zs` is the list created by concatenating `Xs` and `Ys`.
• When you concatenate the empty list with anything, you get that thing.
```concat([],Ys,Ys).
```

• 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]) :-
concat(Xs,Ys,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 `member`.
• 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.

## Functions and Patterns

• 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 `node` and `empty` to define trees.
```in_tree(Val,empty) :- fail.
in_tree(Val,node(Val,Less,Greater)).
in_tree(Val,node(NodeVal,Less,Greater)) :-
Val < NodeVal, in_tree(Val,Less).
in_tree(Val,node(NodeVal,Less,Greater)) :-
Val > NodeVal, in_tree(Val,Greater).
```

• Think about how you'd write `insert` and `delete`.

## Input and Output

• 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 `display`).
• The `write(X)` predicate always holds and has the side effect of writing its argument to stdout.
• The `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.
showpath(Val,node(Val,Less,Greater)) :-
write(Val), nl.
showpath(Val,node(NodeVal,Less,Greater)) :-
Val < NodeVal, write(NodeVal), nl, showpath(Val,Less).
showpath(Val,node(NodeVal,Less,Greater)) :-
Val > NodeVal, write(NodeVal), nl, showpath(Val,Greater).
```

## Implementation

• 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)
Repeatedly
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.

On to Perspectives on Object-Oriented Programming
Back to Logic Programming in Prolog
Outlines: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
Current position in syllabus

[Instructions] [Search] [Current] [Changes] [Syllabus] [Handouts] [Outlines] [Assignments]

Disclaimer Often, these pages were created "on the fly" with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.