440 likes | 2.43k Views
CS 480/580: Artificial Intelligence, Prof. Cindy Marling. 2. States of the World as Lists. Each state is a list of four atoms, each with the value of e (for east) or w (for west)In LISP, we construct these lists and access each element as follows. (defun make-state (f w g c) (list f w g c))(
E N D
1. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 1 The Farmer, Wolf, Goat and Cabbage Revisited Recall that we have already solved this problem in Prolog
A farmer with his wolf, goat, and cabbage come to the edge of a river they
wish to cross. There is a boat at the rivers edge, but, of course, only the
farmer can row. The boat also can carry only two things (including the
rower) at a time. If the wolf is ever left alone with the goat, the wolf will eat
the goat; similarly, if the goat is left alone with the cabbage, the goat will eat
the cabbage. Devise a sequence of crossings of the river so that all four
characters arrive safely on the other side of the river.
2. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 2 States of the World as Lists Each state is a list of four atoms, each with the value of e (for east) or w (for west)
In LISP, we construct these lists and access each element as follows
3. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 3 Implementing FWGC in LISP We define a function opposite, so that characters can cross the river
(defun opposite (side)
(cond ((equal side 'e) 'w)
((equal side 'w) 'e)))
We also need a function safe, so that nothing gets eaten. A safe input state is returned unchanged, while nil is returned for an unsafe state.
4. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 4 The Four Move Functions There are four possible moves, just as there were in Prolog
The farmer can row himself, the wolf, the goat or the cabbage across the river
To move something other than himself, that thing must be on the same side of the river as he is. Otherwise, an attempted move returns nil.
For the farmer to move the wolf, for example, we have:
5. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 5 Backtracking Search of the State Space Backtracking is a built-in search strategy in Prolog, but not in LISP
This is both advantageous and disadvantageous
When you want backtracking search, you have to implement it yourself
When you want some other search strategy, you can flexibly implement it
A simple (but incomplete) path function is:
6. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 6 Improving the Path Function Three improvements are necessary to implement a working function
We need a test for nil, since you cant reach the goal from nil
We need to keep track of where weve been, so we dont loop
We need to return the path taken, rather than just success or nil
The improved version is:
7. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 7 Putting it All Together A top level function to start the search is defined:
(defun solve-fwgc (state goal)
(path state goal nil) ; we call path with our current state,
) ; the goal state, and an empty been list
This implementation now works like the one we built in Prolog
Code is available online
Running and tracing the code is instructive!
8. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 8 Higher Order Functions One advantage of functional programming languages is the ability to use higher order functions
A higher order function may take functions as parameters and may return a function as a result
A filter is a function that tests each element of a list and removes those elements that fail the test
We have already seen filter-negatives. Here is filter-evens:
9. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 9 A Higher Order Filter Function We can write a higher order filter function to filter based on any test, using funcall
Funcalls first argument is the name of a function to call, and its remaining arguments are the arguments for the called function
The filter function is:
(defun filter (list-of-elements test)
(cond ((null list-of-elements) nil)
((funcall test (car list-of-elements))
(cons (car list-of-elements) (filter (cdr list-of-elements) test)))
(t (filter (cdr list-of-elements) test))))
10. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 10 Using filter and funcall Now we can filter a list using different tests
Ex: > (filter (1 3 -9 5 -2 -7 6) #plusp)
(1 3 5 6)
> (filter (1 2 3 4 5 6 7 8 9) #evenp)
(2 4 6 8)
> (filter (1 a b 3 c 4 7 d) #numberp)
(1 3 4 7)
Note that when the name of a function is passed in as a parameter, it is preceded by #, rather than just a quote, to indicate that it is a function
Note, too, that the result of evaluating a funcall is just what you would get if you evaluated the passed in function with the passed in arguments
Ex: > (member 2 (1 2 3))
(2 3)
> (funcall #member 2 (1 2 3))
(2 3)
11. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 11 Maps A map takes a list of elements and applies a function to each element in the list. It then returns a list of the results.
Example
(defun map-simple (func list)
(cond ((null list) nil)
(t (cons (funcall func (car list)) (map-simple func (cdr list))))))
> (map-simple #1+ (1 2 3 4 5 6))
(2 3 4 5 6 7)
> (map-simple #listp (1 2 (3 4) 5 (6 7 8)))
(NIL NIL T NIL T)
12. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 12 More on Maps and Functions as Arguments mapcar is a built in mapping function that applies a function with one or more arguments to one or more argument lists
Ex: > (mapcar #'< '(5 8 3 7) '(2 9 5 6))
(NIL T T NIL)
> (mapcar #'+ '(2 4 3) '(4 1 1) '(3 2 1))
(9 7 5)
When you have a small and/or simple function you want to apply, you can use a lambda expression instead of defining a function with defun
A lambda expression is a nameless throwaway function
Ex: >(funcall #'(lambda (x y z) (list x x y y z z)) 'cat 'dog 'fish))
(CAT CAT DOG DOG FISH FISH)
>(funcall #'(lambda (x) (append x (reverse x))) '(1 2 3))
(1 2 3 3 2 1)
>(mapcar #'(lambda (x y) (/ (+ x y) 2)) '(7 4 9) '(3 2 5))
(5 3 7)
13. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 13 You Try It! What will the LISP interpreter reply?
1. (funcall #'cdr '(LISP is fun))
2. (mapcar #'numberp '(CS 480 580 AI))
3. (mapcar #'* '(1 2 3) '(4 5 6))
4. (funcall #'(lambda (x) (list x x x)) '(a b))
5. (mapcar #'(lambda (x y z) (* x y z)) '(2 3) '(1 5) '(4 2))
6. (mapcar #'(lambda (x) (and (plusp x) (evenp x))) '(2 3 -6 8 -5))
14. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 14 Search in LISP We now know enough LISP to implement breadth-first, depth-first and best-first search
We will review only breadth-first search together. All three searches are available in the example LISP code directory.
Recall that the algorithm for breadth-first search was presented in Chapter 3
15. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 15 Breadth-First Search Algorithm
16. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 16 Breadth-First Search in LISP: Initial Outline (defun run-breadth (start goal)
(setq *open* (list start))
(setq *closed* nil)
(setq *goal* goal)
(breadth-first))
(defun breadth-first ()
(cond ((null *open*) nil)
(t (let ((state (car *open*)))
(cond ((equal state *goal*) 'success)
(t (setq *closed* (cons state *closed*))
(setq *open* (append (cdr *open*)
(generate-descendants state *moves*)))
(breadth-first)))))))
Here, global variables (denoted *<variable-name>*) are used for the open and closed lists, for possible moves, and for the goal state
We still need to see how to specify allowable moves, how to generate descendents, and how to return the path from start to goal
17. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 17 More LISP Implementation Since moves are functions and we can use functions as parameters, we can establish legal moves by listing available functions
Ex: For the Farmer, Wolf, Goat and Cabbage problem, we have:
(setq *moves*
'(farmer-takes-self farmer-takes-wolf farmer-takes-goat farmer-takes-cabbage))
This allows us to use the same breadth-first search algorithm to solve different problems
We keep the breadth-first search code and just write new functions for the allowable moves
18. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 18 Defining State Records In order to print out the path to a solution, rather than just success, we need to keep track of each states parent as we traverse the state space graph
We do this the same way we built and accessed database records
19. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 19 Using State Records Now we can use the state records to track where weve been as we generate descendents
20. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 20 Printing out the Solution Path Now that we have tracked each childs parent, we are able to trace a path from the start state to the goal state
When we reach the goal state, we trace this path as follows:
21. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 21 The Final Version of Breadth-First Search (defun run-breadth (start goal moves)
(setq *open* (list (build-record start nil)))
(setq *closed* nil)
(setq *goal* goal)
(setq *moves* moves)
(breadth-first))
22. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 22 Pattern Matching in LISP Pattern matching has many uses, in applications like text processing and cryptography, as well as for AI problems like unification and natural language understanding (including fun things like chat bots)
The matching process given here is a simplification of Prolog unification
Instead of named variables, it allows only the variable ? (question mark)
We can then easily see if a pattern is a variable with the predicate:
(defun variable-p (x)
(equal x '?)
)
The code for testing if two atomic patterns match is:
(defun match-atom (pattern1 pattern2)
(or (equal pattern1 pattern2) ; both patterns are the same, or
(variable-p pattern1) ; one of them is a variable
(variable-p pattern2)))
23. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 23 Matching Arbitrary Patterns Any pattern can be represented as an S-expression. This allows matching of any arbitrary patterns.
(defun match (pattern1 pattern2)
(cond (or (atom pattern1) (atom pattern2)) ; a pattern is atomic
(match-atom pattern1 pattern2) ; so match atoms
(t (and (match (car pattern1) (car pattern2)) ; otherwise, recurse (match (cdr pattern1) (cdr pattern2)))))) ; to match car and cdr
> (match (likes cindy chocolate) (likes cindy chocolate))
T
> (match (likes cindy chocolate) (likes cindy cats))
NIL
> (match (likes cindy ?) (likes cindy chocolate))
T
> (match (likes ? chocolate) (likes cindy ?))
T
24. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 24 A Matching Application for Databases Simple matching capabilities can be combined to implement useful systems
The database example we discussed previously can be enhanced by adding a function to retrieve matching records
A global employee database is first established
> (setq *database* (((lovelace ada) 50000.00 1234)
((turing alan) 45000.00 3927)
((shelley mary) 35000.00 2850)
((vonNeumann john) 40000.00 7955)
((simon herbert) 50000.00 1374)
((mccarthy john) 48000.00 2864)
((russell bertrand) 35000.00 2950))
*DATABASE*
25. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 25 Finding Matching Database Records (defun get-matches (pattern database)
(cond ((null database) ())
((match pattern (car database)) ; match found, add to result
(cons (car database) (get-matches pattern (cdr database))))
(t (get-matches (cdr database)))))
> (get-matches ((turing alan) 45000.00 3927) *database*)
((TURING ALAN) 45000.00 3927)
> (get-matches (? 50000.00 ?) *database*) ; everyone who makes $50,000
(((LOVELACE ADA) 50000.00 1234) ((SIMON HERBERT) 50000.00 1374))
> (get-matches ((? John) ? ?) *database*) ; everyone named John
(((VONNEUMANN JOHN) 40000.00 7955) ((MCCARTHY JOHN) 48000.00 2864))
26. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 26 A Recursive Unification Function in LISP Recall from Chapter 2 that unification is a type of pattern matching
Unification is an algorithm for determining the substitutions that make two predicate calculus expressions match
Ex: (likes ted holidays) unifies with (likes ted X) under the substitution {holidays/X}
To unify, the expressions must have the same predicate and the same number of terms
Different constants do not match each other, but identical ones do
A variable can match a constant, another variable, or a function expression, as long as the variable does not appear within that expression
substituting (p X) for X would lead to the infinite (p (p (pX)))
Implementing this algorithm in LISP is not hard. Because LISP is not case sensitive, we just need another way to indicate variables, perhaps by starting their names with special characters or by preceding their names with the marker var
27. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 27 Unification Algorithm
28. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 28 Implementation in LISP The LISP function unify has three arguments, two patterns to be matched, and a list of bindings (previously made substitutions)
We need to know the substitutions that have already been made, so that, for example, once holidays is substituted for X, X keeps that value throughout the whole expression
At the start, the list of bindings is an empty list
The LISP function returns a list of substitutions made, or nil, when the patterns already match without any substitutions, or failed, when it is not possible to unify the two expressions
29. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 29 Examples of Unification Function Output > (unify (p a (var x)) (p a b) ())
(((VAR X) . B)) ; returns substitution of b for (var x)
> (unify (p (var y) b) (p a (var x)) ()) ; variables appear in both patterns
(((VAR X) . B) ((VAR Y) . A)
> (unify (p a) (p a)) ())
NIL ; NIL indicates no substitution required
> (unify (p a) (q a) ())
FAILED ; The atom FAILED is returned to
; indicate unification is not possible
30. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 30 The unify Function (defun unify (pattern1 pattern2 substitution-list)
(cond ((equal substitution-list 'failed) 'failed)
((varp pattern1) (match-var pattern1 pattern2 substitution-list))
((varp pattern2) (match-var pattern2 pattern1 substitution-list))
((is-constant-p pattern1)
(cond ((equal pattern1 pattern2) substitution-list)
(t 'failed)))
((is-constant-p pattern2) 'failed)
(t (unify (cdr pattern1) (cdr pattern2)
(unify (car pattern1) (car pattern2) substitution-list)))))
31. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 31 The varp, is-constant-p, and match-var Functions (defun match-var (var pattern substitution-list)
(cond ((equal var pattern) substitution-list)
(t (let ((binding (get-binding var substitution-list)))
(cond (binding (unify (get-binding-value binding) pattern substitution-list))
((occursp var pattern) 'failed)
(t (acons var pattern substitution-list)))))))
32. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 32 The occurs-p Function and Association Lists
An association list, or a-list, is a built-in LISP data type
It is a list of key/data records, represented as lists or dotted pairs
There is a built in function assoc, which returns a record based on its key
> (assoc 3 ((1 a) (2 b) (3 c) (4 d)))
(3 C)
> (assoc d ((a b c) (b c d e) (d e f) (c d e)))
(D E F)
>(assoc c ((a . 1) (b . 2) (c . 3) (d . 4)))
(c . 3)
33. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 33 Dotted Pairs A dotted pair is just the result of consing one s-expression onto another
We usually prefer not to use dotted pair notation
Ex: (1 2 3) looks better than (1 . (2 . (3 . nil)))
The a-list is one place dotted pairs are useful, because they allow you to cons together two atoms to associate a key with one value
> (cons 'senior 2005)
(SENIOR . 2005)
> (car '(senior . 2005))
SENIOR
> (cdr '(senior . 2005))
2005
Note that this allows the cdr of a list to be an atom
We use cdr to get the value(s) associated with a key in an a-list
34. CS 480/580: Artificial Intelligence, Prof. Cindy Marling 34 The acons Function and Using a-lists for Bindings The acons function inserts a key and some data as a record at the beginning of an a-list
> (acons 'ai 480 nil)
((AI . 480))
> (acons 'ai 480 '((db . 462) (pl . 320)))
((AI . 480) (DB . 462) (PL . 320))
> (cdr (assoc 'ai '((AI . 480) (DB . 462) (PL . 320)))))
480
a-lists help manage the bindings in unification