LPN: reorg, split notes from exocortex page.
The exocortex page was getting unwieldy, this makes more sense.
This commit is contained in:
parent
36ed6df77f
commit
6d4fb673ef
|
@ -0,0 +1,97 @@
|
|||
## Some basic syntax
|
||||
|
||||
```
|
||||
property(name).
|
||||
proposition.
|
||||
/* rules */
|
||||
head :- tail.
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
happy(yolanda).
|
||||
listens2Music(mia).
|
||||
listens2Music(yolanda):- happy(yolanda).
|
||||
playsAirGuitar(mia):- listens2Music(mia).
|
||||
playsAirGuitar(yolanda):- listens2Music(yolanda).
|
||||
```
|
||||
|
||||
### Rules
|
||||
|
||||
Conditional fact:
|
||||
|
||||
```
|
||||
/* source */
|
||||
listensToMusic(X) :- happy(X).
|
||||
playsAirGuitar(X) :- listensToMusic(X).
|
||||
happy(a).
|
||||
/* interactive */
|
||||
playsAirGuitar(a). /* true */
|
||||
playsAirGuitar(b). /* false */
|
||||
```
|
||||
|
||||
## KB5
|
||||
|
||||
```
|
||||
loves(vincent,mia).
|
||||
loves(marsellus,mia).
|
||||
loves(pumpkin,honey_bunny).
|
||||
loves(honey_bunny,pumpkin).
|
||||
jealous(X,Y):- loves(X,Z), loves(Y,Z).
|
||||
```
|
||||
|
||||
Note that when you enter queries, sometimes it waits for you to enter something
|
||||
(so far seems like a semi-colon finishes a query). For example:
|
||||
|
||||
```
|
||||
2 ?- jealous(vincent, W).
|
||||
W = vincent
|
||||
```
|
||||
|
||||
Output freezes there, waiting on input. Enter the semicolon:
|
||||
|
||||
```
|
||||
2 ?- jealous(vincent, W).
|
||||
W = vincent ;
|
||||
W = marsellus.
|
||||
```
|
||||
|
||||
Replacing both atoms with variable:
|
||||
|
||||
```
|
||||
3 ?- jealous(A, B).
|
||||
A = B, B = vincent ;
|
||||
A = vincent,
|
||||
B = marsellus ;
|
||||
A = marsellus,
|
||||
B = vincent ;
|
||||
A = B, B = marsellus ;
|
||||
A = B, B = pumpkin ;
|
||||
A = B, B = honey_bunny.
|
||||
```
|
||||
|
||||
This brings up something interesting: *jealous(A, A)* is always true:
|
||||
|
||||
```
|
||||
4 ?- jealous(A, A).
|
||||
A = vincent ;
|
||||
A = marsellus ;
|
||||
A = pumpkin ;
|
||||
A = honey_bunny.
|
||||
```
|
||||
|
||||
Can we fix that in the definitions?
|
||||
|
||||
```
|
||||
jealous(X,Y):- loves(X,Z), loves(Y,Z), X \= Y.
|
||||
```
|
||||
|
||||
Now:
|
||||
|
||||
```
|
||||
9 ?- jealous(A, A).
|
||||
false.
|
||||
```
|
||||
|
||||
Re-reading a previous section, seems that ';' means "or".
|
|
@ -0,0 +1,50 @@
|
|||
## Unification
|
||||
|
||||
Two terms unify if they are
|
||||
1. The same term
|
||||
2. They contain variables that can be uniformly instantiated such that the
|
||||
resulting terms are equal.
|
||||
|
||||
Unification is foundational to Prolog.
|
||||
|
||||
`=/2` checks whether its two arguments unify:
|
||||
|
||||
```
|
||||
12 ?- =(a, a).
|
||||
true.
|
||||
13 ?- =(a, b).
|
||||
false.
|
||||
```
|
||||
|
||||
*NB* variable assignment with `=`:
|
||||
|
||||
```
|
||||
24 ?- vincent = X.
|
||||
X = vincent.
|
||||
25 ?- jealous(Y, X).
|
||||
Y = vincent,
|
||||
X = marsellus ;
|
||||
Y = marsellus,
|
||||
X = vincent ;
|
||||
false.
|
||||
```
|
||||
|
||||
### Lines
|
||||
```
|
||||
/* lines.pl */
|
||||
vertical(line(point(X,Y),point(X,Z))).
|
||||
horizontal(line(point(X,Y),point(Z,Y))).
|
||||
```
|
||||
|
||||
Check this out:
|
||||
|
||||
```
|
||||
39 ?- vertical(line(point(1, 7), P)).
|
||||
P = point(1, _402).
|
||||
```
|
||||
|
||||
The answer is structured: `_402` is a placeholder variable that means "any old
|
||||
value of Y will do." This answer was derived solely through unification, and
|
||||
didn't require logic (e.g. modus ponens) or any sort of mathematical
|
||||
relationships. "Moreover, when a program is written that makes heavy use of
|
||||
unification, it is likely to be extremely efficient."
|
|
@ -0,0 +1,24 @@
|
|||
%% facts
|
||||
child(anne,bridget).
|
||||
child(bridget,caroline).
|
||||
child(caroline,donna).
|
||||
child(donna,emily).
|
||||
|
||||
%% rules
|
||||
descend(X,Y) :- child(X,Y).
|
||||
|
||||
descend(X,Y) :- child(X,Z),
|
||||
descend(Z,Y).
|
||||
|
||||
descend1(X,Y) :- child(X,Y).
|
||||
descend1(X,Y) :- child(X,Z),
|
||||
descend1(Z,Y).
|
||||
|
||||
descend2(X,Y) :- child(X,Y).
|
||||
descend2(X,Y) :- descend2(Z,Y),
|
||||
child(X,Z).
|
||||
|
||||
%% WARNING: non-terminating.
|
||||
descend3(X,Y) :- child(X,Y).
|
||||
descend3(X,Y) :- descend3(X,Z),
|
||||
descend3(Z,Y).
|
|
@ -0,0 +1,2 @@
|
|||
%% infinite recursion.
|
||||
p :- p.
|
|
@ -0,0 +1,7 @@
|
|||
directlyIn(natasha, irina).
|
||||
directlyIn(olga, natasha).
|
||||
directlyIn(katarina, olga).
|
||||
|
||||
in(X, Y) :- directlyIn(X, Y).
|
||||
in(X, Y) :- directlyIn(X, Z),
|
||||
in(Z, Y).
|
|
@ -0,0 +1,162 @@
|
|||
## Recursion
|
||||
|
||||
Recursive definitions require that the recursive function isn't the first
|
||||
clause, ex:
|
||||
|
||||
```
|
||||
is_digesting(X,Y) :- just_ate(X,Y).
|
||||
is_digesting(X,Y) :-
|
||||
just_ate(X,Z),
|
||||
is_digesting(Z,Y).
|
||||
```
|
||||
|
||||
Recursive definition require a base ("escape") clause in addition to the
|
||||
recursive clause.
|
||||
|
||||
### Rule ordering, goal ordering, and termination
|
||||
|
||||
Underlying vision for Prolog (and logic programming in general): the programmer
|
||||
should be able to describe problems, and the computer derives the solution. The
|
||||
programmer builds a knowledge base, then asks question of the computer.
|
||||
|
||||
Prolog's search order:
|
||||
|
||||
1. KB from top to bottom
|
||||
2. Clauses from left to right
|
||||
3. Backtracks on bad choices
|
||||
|
||||
This is somewhat of a procedural approach to declarative programming which may
|
||||
affect the answer. Compare `descend1` with `descend2`:
|
||||
|
||||
```
|
||||
child(anne,bridget).
|
||||
child(bridget,caroline).
|
||||
child(caroline,donna).
|
||||
child(donna,emily).
|
||||
|
||||
descend1(X,Y) :- child(X,Y).
|
||||
descend1(X,Y) :- child(X,Z),
|
||||
descend1(Z,Y).
|
||||
|
||||
descend2(X,Y) :- descend2(Z,Y),
|
||||
child(X,Z).
|
||||
|
||||
descend2(X,Y) :- child(X,Y).
|
||||
```
|
||||
|
||||
Supposedly these give different answers, but `swipl` exploded on the latter...
|
||||
and continuing reading, that's the point --- a warning to avoid left-recursive
|
||||
rules.
|
||||
|
||||
The takeaway is that you can get the overall idea of how to write the program
|
||||
by approaching the problem declaratively. Sensible goal orderings ensure
|
||||
program / query termination: place the recursive definition last.
|
||||
|
||||
### Exercises
|
||||
|
||||
#### Exercise 3.1
|
||||
|
||||
In the text, we discussed the predicate
|
||||
|
||||
```
|
||||
descend(X,Y) :- child(X,Y).
|
||||
descend(X,Y) :- child(X,Z),
|
||||
descend(Z,Y).
|
||||
```
|
||||
|
||||
Suppose we reformulated this predicate as follows:
|
||||
|
||||
```
|
||||
descend(X,Y) :- child(X,Y).
|
||||
descend(X,Y) :- descend(X,Z),
|
||||
descend(Z,Y).
|
||||
```
|
||||
|
||||
Would this be problematic?
|
||||
|
||||
**A**: yes: there's no termination clause. See `descend3` in `descend.pl`.
|
||||
|
||||
#### Exercise 3.2
|
||||
|
||||
Do you know these wooden Russian dolls (Matryoshka dolls) where the smaller
|
||||
ones are contained in bigger ones?
|
||||
|
||||
Ex katarina(olga(natasha(irina))).
|
||||
|
||||
First, write a knowledge base using the predicate `directlyIn/2` which encodes
|
||||
which doll is directly contained in which other doll. Then, define a recursive
|
||||
predicate `in/2`, that tells us which doll is (directly or indirectly)
|
||||
contained in which other dolls. For example, the query `in(katarina,natasha)`
|
||||
should evaluate to true, while `in(olga, katarina)` should fail.
|
||||
|
||||
```
|
||||
directlyIn(natasha, irina).
|
||||
directlyIn(olga, natasha).
|
||||
directlyIn(katarina, olga).
|
||||
|
||||
in(X, Y) :- directlyIn(X, Y).
|
||||
in(X, Y) :- directlyIn(X, Z),
|
||||
in(Z, Y).
|
||||
```
|
||||
|
||||
#### Exercise 3.3
|
||||
|
||||
We have the following knowledge base:
|
||||
|
||||
```
|
||||
directTrain(saarbruecken,dudweiler).
|
||||
directTrain(forbach,saarbruecken).
|
||||
directTrain(freyming,forbach).
|
||||
directTrain(stAvold,freyming).
|
||||
directTrain(fahlquemont,stAvold).
|
||||
directTrain(metz,fahlquemont).
|
||||
directTrain(nancy,metz).
|
||||
```
|
||||
|
||||
That is, this knowledge base holds facts about towns it is possible to travel
|
||||
between by taking a direct train. But of course, we can travel further by
|
||||
chaining together direct train journeys. Write a recursive predicate
|
||||
`travelFromTo/2` that tells us when we can travel by train between two towns.
|
||||
For example, when given the query `travelFromTo(nancy,saarbruecken)` it should
|
||||
reply yes.
|
||||
|
||||
```
|
||||
travelFromTo(X, Y) :- directTrain(X, Y).
|
||||
travelFromTo(X, Y) :- directTrain(X, Z),
|
||||
travelFromTo(Z, Y).
|
||||
```
|
||||
|
||||
#### Exercise 3.4
|
||||
|
||||
Define a predicate `greater_than/2` that takes two numerals in the notation
|
||||
that we introduced in the text (that is, `0`, `succ(0)`, `succ(succ(0))`, and
|
||||
so on) as arguments and decides whether the first one is greater than the
|
||||
second one. For example:
|
||||
|
||||
```
|
||||
?- greater_than(succ(succ(succ(0))),succ(0)).
|
||||
yes
|
||||
?- greater_than(succ(succ(0)),succ(succ(succ(0)))).
|
||||
no
|
||||
```
|
||||
|
||||
#### Exercise 3.5
|
||||
|
||||
Binary trees are trees where all internal nodes have exactly two children. The
|
||||
smallest binary trees consist of only one leaf node. We will represent leaf
|
||||
nodes as `leaf(Label)`. For instance, `leaf(3)` and `leaf(7)` are leaf nodes,
|
||||
and therefore small binary trees. Given two binary trees B1 and B2 we can
|
||||
combine them into one binary tree using the functor `tree/2` as follows:
|
||||
`tree(B1,B2)`. So, from the leaves `leaf(1)` and `leaf(2)` we can build the
|
||||
binary tree `tree(leaf(1),leaf(2))` . And from the binary trees
|
||||
`tree(leaf(1),leaf(2))` and `leaf(4)` we can build the binary tree
|
||||
`tree(tree(leaf(1), leaf(2)),leaf(4))`.
|
||||
|
||||
Now, define a predicate `swap/2`, which produces the mirror image of the binary
|
||||
tree that is its first argument. For example:
|
||||
|
||||
```
|
||||
?- swap(tree(tree(leaf(1), leaf(2)), leaf(4)),T).
|
||||
T = tree(leaf(4), tree(leaf(2), leaf(1))).
|
||||
yes
|
||||
```
|
|
@ -0,0 +1,8 @@
|
|||
numeral(0).
|
||||
numeral(succ(X)) :- numeral(X).
|
||||
|
||||
%% add/3: add(X, Y, Z) -> X + Y = Z
|
||||
add(0, X, X).
|
||||
add(succ(X), Y, succ(Z)) :-
|
||||
add(X, Y, Z).
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
%% Given facts.
|
||||
directTrain(saarbruecken,dudweiler).
|
||||
directTrain(forbach,saarbruecken).
|
||||
directTrain(freyming,forbach).
|
||||
directTrain(stAvold,freyming).
|
||||
directTrain(fahlquemont,stAvold).
|
||||
directTrain(metz,fahlquemont).
|
||||
directTrain(nancy,metz).
|
||||
|
||||
%% Rules.
|
||||
travelFromTo(X, Y) :- directTrain(X, Y).
|
||||
travelFromTo(X, Y) :- directTrain(X, Z),
|
||||
travelFromTo(Z, Y).
|
Loading…
Reference in New Issue