Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Test of equality TDDC65 Artificial intelligence and Lisp Lecture 2 in Lisp Recursion, symbols and lists (chapter 4, 5, 6 Haraldssons book) list functions local variables and local functions recursion over sequences * patterns * recursive / iterative process * double recursion * back tracking (= 5 5) - numbers (eq 'a 'a) - symbols (or pointers) (eql 5 5) - atoms (eql #\a #\a) (eql 'a 'a) (equal '(a b c) '(a b c)) - lists Test of data types the function cons graphical representation of lists the list as a binary tree traversing formulas Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson (numberp 5) - number? (symbolp 'a) - symbol? (atom 5) - atom? = not a list? (atom 'a) (listp '(a b c)) - list? Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson To create lists The primitive (cons 'a '(b c)) => (a b c) Common Lisp functions for lists Some of them: Is an element on a list? Concatenate 2 (or more) lists (append '(a b) '(x y)) => (a b x y) Create a list of a fixed number of elements (list 'a (1+ 4) (first '(b c))) => (a 5 b) Constant list '(a b c d) or (quote (a b c d)) Observe the difference. What is? (cons '(1 2) '(a b)) => ? (append '(1 2) '(a b)) => ? (member 'x '(a b x c)) => true Remove all occurrences of an element (remove 'q '(a b q r q)) => (a b r) Reverse the elements on a list (reverse '(a b c)) => (c b a) Replace all occurrences of an element (subst 'new-q 'q '(a q (b q))) => (a new-q (b new-q)) Find the last part of a list (last '(ett två tre)) => (tre) Find the n+1 element (nth '(a b c d) 2) => c (list '(1 2) '(a b)) => ? Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson How to add a new last element? Local variables Put five last on the list (one two three four). Example: (append '(one two three four) (list 'five)) => (one two three four five) (let ((var expr) (var expr) ...) expr) Avoid double calculations: Define a function add-to-the-end (defun add-to-the-end (e l) (append l (list e))) What happens here? (setq number-list '(one two three four)) (add-to-the-end 'five number-list) => (one two three four five) number-list => ? Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson g(x) = sin(f(x)) + cos(f(x)) (defun g (x) (let ((f-value (f x))) (+ (sin f-value) (cos f-value))) After the let-expression the binding disappear between the variable and value. Introduce a local name for a value: (let ((vocals ’(a e i o u å ä ö))) .... (member (first l) vocals) ... ) Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Local functions (labels ((fn (argument) body)) (fn (argument) body)) ...) expression) Example: f(x,y) = x!/y2 (defun f (x y) (labels ((square (n) (* n n)) (fak (n) (if (= n 0) 1 (* n (fak (- n 1)))))) (/ (fak x) (square y)))) The function definitions are only valid inside f! Example: When we have iterative process the recursive function is defined locally. (defun fak (n) (fak-iter n 1)) (defun fak-iter (n res) (if (= n 0) res (fak-iter (- n 1) (* n res)))) (defun fak (n) (labels ((fak-iter (n res) (if (= n 0) res (fak-iter (- n 1) (* n res)))) ) (fak-iter n 1))) Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Sequential processing of lists recursive process Sequential processing of lists iterative process (defun fn (l) (fn-iter l ”init-value”)) (defun fn-sekv (l) (cond ((endp l) “init-value”) (”other condition” ”expression”) (t (”operation” (first l) (fn-sekv (rest l)) ))) Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson (defun fn-iter (l res) (cond ((endp l) res) (”other condition” ”expression”) (t (fn-iter (rest l) (”operation” (first l) res)) ))) Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Example iterative process: recursive process: Replace the first occurrence of a given element (change 'karl ’kalle '(lisa per kalle stina)) => (lisa per karl stina) (defun change (new old l) (cond ((endp l) ’()) ((eq old (first l)) (cons new (rest l))) (t (cons (first l) (change new old (rest l))) ))) (defun change (new old l) (change-iter new old l ’())) (defun change-iter (n g l res) (cond ((endp l) res) ((eq g (first l)) (append res (cons n (rest l)))) (t (change-iter n g (rest l) (add-to-the-end (first l) res))))) Why add-to-the-end instead of cons? Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson The substitution model for the two solutions: Double recursion Lists with lists as elements recursive process: (change 'karl ’kalle '(lisa per kalle stina)) -> (cons ’lisa (change ’karl ’kalle ’(per kalle stina)) -> (cons ’lisa (cons ’per (change ’karl ’kalle ’(kalle stina))) -> (cons ’lisa (cons ’per (cons ’karl ’(stina)))) -> (cons ’lisa (cons ’per ’(karl stina))) -> (cons ’lisa ’(per karl stina)) => (lisa per karl stina) Pattern for sequences: (defun fn (l) (cond ((endp l) “init-value”) ((atom (first l)) (“operation” (first l) (fn (rest l)))) (t (“operation” (fn (first l)) (fn (rest l)))) )) iterative process: (change 'karl ’kalle '(lisa per kalle stina)) -> (ch-i ’karl ’kalle ’(lisa per kalle stina) ’()) -> (ch-i ’karl ’kalle ’(per kalle stina) ’(lisa)) -> (ch-i ’karl ’kalle ’(kalle stina) ’(lisa per)) -> (ch-i ’karl ’kalle (append ’(lisa per) (cons ’karl ’(stina)))) => (lisa per karl stina) Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Total number of elements (defun symbols-in-seq (l) (cond ((endp l) 0) ((atom (first l)) (+ 1 (symbols-in-seq (rest l)))) (t (+ (symbols-in-seq (first l)) (symbols-in-seq (rest l))) ))) (symbols-in-seq '(a (b c (d e)) f)) => 6 Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Remove all occurrences of a symbol on all levels (defun my-all-remove (x l) (cond ((endp l) ’()) ((atom (first l)) (if (eq x (first l)) (my-all-remove x (rest l)) (cons (first l) (my-all-remove x (rest l)))) (t (cons (my-all-remove x (first l)) (my-all-remove x (rest l))) ))) (my-all-remove 'q '(a q (b q (q)) c)) => (a (b nil) c) Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Back tracking Is a given element somewhere in a list? (exist? 'q '(a (b q) c)) => t (defun exist? (x l) (cond ((endp l) nil) ((atom (first l)) (if (eq x (first l)) t (exist? x (rest l)))) (t (or (exist? x (first l)) (exist? x (rest l))) ))) Some problems are of the nature that you can not directly say if you have reached the right element or not. You must go on. Typical in search problems you must handle back tracking. Search one alternative, if it fails come back and try another alternative. Lab 2 and 3 use this concept. Here we will solve the following problem: Find the element after the last occurrence of a given element. We assume that the given element is not last in the list. (find-after ’x ’(a b x c)) => c (find-after ’x ’(a b x c x d x e)) => e (find-after ’x ’(a b c)) => element-not-on-list Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson (defun find-after (x l) (cond ((endp l) ’element-not-on-list) ((eq x (first l)) ; the element is found (let ((next-value (find-after x (rest l)))) ; check further (if (eq next-value ’element-not-on-list) (second l) next-value))) (t (find-after x (rest l))))) The function cons dotted pair: (cons ’a ’b) => (a . b) dotted list: (cons ’x (cons ’y ’z)) => (x y . z) association list: ((ett . one) (två . two) (tre . three)) Observe that all of these expressions describes the same list: (x y z) = (x y z . nil) = (x y . (z . nil)) = (x . (y . (z . nil))) Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson The list seen as a binary tree The list seen as a binary tree Pattern - binary tree: (defun fn (bt) (if (atom bt) <- leaf? “processing of the leaf ” (“operation” (fn (car bt)) (fn (cdr bt))))) pattern - binary tree - nil a special case: (defun fn (bt) (cond ((eq bt nil) “value”) ((atom bt) “processing of the leaf”) (t (“operation” (fn (car bt)) (fn (cdr bt))))) Observe: Here I use car and cdr instead of first and rest. (Better with primitives left and right) Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Number of elements (as leaves) Here is nil a leaf, it will be counted. (defun count-leaves (bt) (if (atom bt) 1 (+ (count-leaves (car bt)) (count-leaves (cdr bt)) ))) Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Observations Sequence solution: 3 cases - end of list - first element is an atom - first element is a list Binary tree solution Here is nil not a leaf 2 cases - atom (the leaf) - list (intern nod in the tree) (defun count-leaves (bt) (cond ((eq bt nil) 0) ((atom bt) 1) (t (+ (count-leaves (car bt)) (count-leaves (cdr bt)) ))) Observe: The binary tree algorithm is more general, it includes the sequential algorithm. But, of course the problems nature gives what solution to use. Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson An arithmetic formula as a binary tree. * + 2 / 4 8 3 2 We represent the binary tree as a list with the following structure: (left-tree operator right-tree) Define a function to calculate its value (value 3) => 3 (value ’((2 + 4) * ((8 - 2) / 3))) => 12 (defun value (expr) (cond ((number? expr) expr) ((eq (operator expr) ’+) (+ (value (arg1 expr)) (value (arg2 expr)))) ((eq (operator expr) ’-) (- (value (arg1 expr)) (value (arg2 expr)))) ((eq (operator expr) ’*) (* ... ...)) ((eq (operator expr) ’/) (/ ... ...)) (t (error ... error message...)))) (defun number? (expr) (numberp expr)) (defun arg1 (expr) (first expr)) (defun arg2 (expr) (third expr) (defun operator (expr) (second expr))