diff --git a/lpn/kb1.pl b/lpn/ch01/kb1.pl similarity index 100% rename from lpn/kb1.pl rename to lpn/ch01/kb1.pl diff --git a/lpn/kb2.pl b/lpn/ch01/kb2.pl similarity index 100% rename from lpn/kb2.pl rename to lpn/ch01/kb2.pl diff --git a/lpn/kb3.pl b/lpn/ch01/kb3.pl similarity index 100% rename from lpn/kb3.pl rename to lpn/ch01/kb3.pl diff --git a/lpn/kb4.pl b/lpn/ch01/kb4.pl similarity index 100% rename from lpn/kb4.pl rename to lpn/ch01/kb4.pl diff --git a/lpn/kb5.pl b/lpn/ch01/kb5.pl similarity index 100% rename from lpn/kb5.pl rename to lpn/ch01/kb5.pl diff --git a/lpn/lexicon.pl b/lpn/ch01/lexicon.pl similarity index 100% rename from lpn/lexicon.pl rename to lpn/ch01/lexicon.pl diff --git a/lpn/lines.pl b/lpn/ch01/lines.pl similarity index 100% rename from lpn/lines.pl rename to lpn/ch01/lines.pl diff --git a/lpn/ch01/notes.md b/lpn/ch01/notes.md new file mode 100644 index 0000000..5cbf06f --- /dev/null +++ b/lpn/ch01/notes.md @@ -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". diff --git a/lpn/test.pl b/lpn/ch01/test.pl similarity index 100% rename from lpn/test.pl rename to lpn/ch01/test.pl diff --git a/lpn/crossword.pl b/lpn/ch02/crossword.pl similarity index 100% rename from lpn/crossword.pl rename to lpn/ch02/crossword.pl diff --git a/lpn/ch02/notes.md b/lpn/ch02/notes.md new file mode 100644 index 0000000..2739ecd --- /dev/null +++ b/lpn/ch02/notes.md @@ -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." \ No newline at end of file diff --git a/lpn/proof22.pl b/lpn/ch02/proof22.pl similarity index 100% rename from lpn/proof22.pl rename to lpn/ch02/proof22.pl diff --git a/lpn/ch03/descend.pl b/lpn/ch03/descend.pl new file mode 100644 index 0000000..fc31eb1 --- /dev/null +++ b/lpn/ch03/descend.pl @@ -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). \ No newline at end of file diff --git a/lpn/eating.pl b/lpn/ch03/eating.pl similarity index 100% rename from lpn/eating.pl rename to lpn/ch03/eating.pl diff --git a/lpn/ch03/infrecur.pl b/lpn/ch03/infrecur.pl new file mode 100644 index 0000000..3ae8771 --- /dev/null +++ b/lpn/ch03/infrecur.pl @@ -0,0 +1,2 @@ +%% infinite recursion. +p :- p. diff --git a/lpn/ch03/matroshka.pl b/lpn/ch03/matroshka.pl new file mode 100644 index 0000000..3ad9e54 --- /dev/null +++ b/lpn/ch03/matroshka.pl @@ -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). diff --git a/lpn/ch03/notes.md b/lpn/ch03/notes.md new file mode 100644 index 0000000..dde32b2 --- /dev/null +++ b/lpn/ch03/notes.md @@ -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 +``` \ No newline at end of file diff --git a/lpn/ch03/succ.pl b/lpn/ch03/succ.pl new file mode 100644 index 0000000..50b3866 --- /dev/null +++ b/lpn/ch03/succ.pl @@ -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). + diff --git a/lpn/ch03/travel.pl b/lpn/ch03/travel.pl new file mode 100644 index 0000000..01dd410 --- /dev/null +++ b/lpn/ch03/travel.pl @@ -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).