Functional Programming 09 Examples Tower of Hanoi A B C Tower of Hanoi Tower of Hanoi 希望執行結果 > (tower-of-hanoi ‘(3 2 1) ‘a ‘b ‘c) ;三個盤子 柱子a b c 7 第一步 寫外殼 (defun tower-of-hanoi (disks from to spare) ) 第二步 決定遞迴 何時停止? 盤子搬完 還有盤子就搬並計算次數 (繼續遞迴) (if (endp disks) xxx ;沒有盤子就return 0 (需要零次) ooo ;有盤子就繼續搬並計算次數 ) Tower of Hanoi 第三步 決定次數計算方式 xxx? 0 (沒盤子就不必搬了,所以直接return 0) ooo? 先把上面n-1個搬到暫存區spare 要幾次? (tower-of-hanoi (rest disks ) from spare to) 再把最底下那個搬到目的地to 要幾次? 1 再把暫存區的n-1個搬到目的地to 要幾次? (tower-of-hanoi (rest disks ) spare to from) Tower of Hanoi (defun tower-of-hanoi (disks from to spare) (if (endp disks) 0 (+ (tower-of-hanoi (rest disks) from spare to) 1 (tower-of-hanoi (rest disks) spare to from) ) ) ) Tower of Hanoi 希望執行結果 要包含搬法 > (tower-of-hanoi ‘(3 2 1) ‘a ‘b ‘c) ;三個環 柱子a b c Move 1 from A to B. Move 2 from A to C. Move 1 from B to C. Move 3 from A to B. Move 1 from C to A. Move 2 from C to B. Move 1 from A to B. NIL Tower of Hanoi 僅須修改剛剛的ooo 先把上面n-1個搬到暫存區spare 要幾次? (tower-of-hanoi (rest disks ) from spare to) 再把最底下那個搬到目的地to 要幾次? 1 再把暫存區的n-1個搬到目的地to 要幾次? (tower-of-hanoi (rest disks ) spare to from) ->把1 改成 (format t “Move ~A from ~A to ~A.~%” (car disks) from to) 把+去掉改成 (progn ) 但是如果盤子空了要作什麼? 沒要作什麼..所以把if 改成unless..那麼prog可以去掉 Tower of Hanoi (defun tower-of-hanoi (disks from to spare) (unless (endp disks) (tower-of-hanoi (rest disks) from spare to) (format t "Move ~A from ~A to ~A.~%" (car disks) from to) (tower-of-hanoi (rest disks) spare to from) ) ) Inference - Matching > (match ‘(p a b c a) ‘(p ?x ?y c ?x)) ((?Y . B) (?X . A)) T > (match ‘(p ?x b ?y a) ‘(p ?y b c a)) ((?Y . C) (?X . ?Y)) T > (match ‘(a b c) ‘(a a a)) NIL > (match ‘(p ?x) ‘(p ?x)) NIL T > (match ‘(p ?v b ?x d (?z ?z)) ‘(p a ?w c ?y (e e)) ‘((?v . a) (?w . b))) ((?Z . E) (?Y . D) (?X . C) (?V . A) (?W . B)) T > (match ‘(?x a) ‘(?y ?y)) ((?Y . A) (?X . ?Y)) T Inference - Matching 第一步 想match的步驟 If x and y are eql, then match; otherwise, If x is a variable that has a binding, they match if it matches y; otherwise, If y is a variable that has a binding, they match if it matches x; otherwise, If x is a variable(without a binding), they match and thereby establish a binding for it; otherwise, If y is a variable(without a binding), they match and thereby establish a binding for it; otherwise, They match if they are both conses, and the cars match, and the cdrs match with the bindings generated thereby. Inference - Matching 第二步 寫一個可判斷是否為變數的函數 是否為symbol (symbolp x) Symbol的字串中第一個字元是否為’?’ (eql (char (symbol-name x) 0) #\?) (defun var? (x) (and (symbolp x) (eql (char (symbol-name x) 0) #\?))) Inference - Matching 回顧 assoc > (setq x (cons ‘a ‘b)) (A . B) > (setq y (cons ‘c ‘d)) (C . D) > (setq q (list x y)) ((A . B) (C . D)) > (assoc ‘a q) (A . B) Inference - Matching If x and y are eql, then match; ((eql x y) (values binds t)) If x is a variable that has a binding, they match if it matches y; ((assoc x binds) (match (binding x binds) y binds)) If y is a variable that has a binding, they match if it matches x; ((assoc y binds) (match x (binding y binds) binds)) If x is a variable (without a binding), they match and thereby establish a binding for it; ((var? x) (values (cons (cons x y) binds) t)) ;把新的bind(關於x)加入binds If y is a variable (without a binding), they match and thereby establish a binding for it; ((var? y) (values (cons (cons y x) binds) t)) ;把新的bind (關於y)加入binds Inference - Matching They match if they are both conses, and the cars match, and the cdrs match with the bindings generated thereby. (when (and (consp x) (consp y)) ;x與y都是cons (multiple-value-bind (b2 yes) ;把b2與yes bind 到 (match (car x) (car y) binds) ;(car x)與(car y)的match (and yes (match (cdr x) (cdr y) b2)))) ;若(car x) 與(car y)已經match,也就是yes為T,則再接 ;著match (cdr x) (cdr y),且已經將binding更新為b2 Inference - Matching (defun match (x y &optional binds) (cond ((eql x y) (values binds t)) ((assoc x binds) (match (binding x binds) y binds)) ((assoc y binds) (match x (binding y binds) binds)) ((var? x) (values (cons (cons x y) binds) t)) ((var? y) (values (cons (cons y x) binds) t)) (t (when (and (consp x) (consp y)) (multiple-value-bind (b2 yes) (match (car x) (car y) binds) (and yes (match (cdr x) (cdr y) b2))))))) Inference - Matching 第三步 找某個變數的binding方式 (defun binding (x binds) (let ((b (assoc x binds))) ;找binds裡頭有x的,例如 (x, □) (if b ;如果有找到,知道被bind到(cdr b) (or (binding (cdr b) binds) ;繼續找看看(cdr b) bind 到誰,例如(□, ◎) (cdr b))))) ;往下沒有可繼續bind的話,則 就是(cdr b),例如 □ Inference - Answering Queries 第四步 回答誰誰誰符合某種關係 > (parent ?x ?y) (((?x . donald) (?y . nancy)) 呼叫<-以加入rule或fact並且return目前關於某個predicate 的rule有幾個 > (<- (parent donald nancy)) 1 > (<- (child ?x ?y) (parent ?y ?x)) 1 Inference - Answering Queries (defvar *rules* (make-hash-table)) ;定義一個全域變數為hash table (defmacro <- (con &optional ant) ;定義一個巨集<- `(length (push (cons (cdr ',con) ',ant) (gethash (car ',con) *rules*)))) If you prefix a comma to something within a ` (backquoated) expression, it will be evaluated > `(a b c) (A B C) > (setf a 1 b 2) 2 > `(a is ,a and b is ,b) (A IS 1 AND B IS 2) Inference - Answering Queries (defun prove (expr &optional binds) ;expr 可能是(parent ?x ?y)或 (and ( ) ( ) ( )) (case (car expr) ;看是否為rule(有and or not) (and (prove-and (cdr expr) binds)) (or (prove-or (cdr expr) binds)) (not (prove-not (cadr expr) binds)) (t (prove-simple (car expr) (cdr expr) binds)))) ;不是rule,例如(parent ?x ?y) (defun prove-simple (pred args binds) ; parent (?x ?y) binds (mapcan #'(lambda (r) (multiple-value-bind (b2 yes) ;分別把match的binding結果以及 (match args (car r) ; 成功與否給b2與yes binds) (when yes ;如果有成功binding (if (cdr r) ;如果是rule,即 r是像 (and (male ?x) ( ) ) (prove (cdr r) b2) ;要以b2為新的binds,繼續prove (list b2))))) ;return (b2) (mapcar #‘change-vars ;把以pred為key的那些rule找出來, (gethash pred *rules*)))) ;並且把其中的變數換成新產生的 Inference - Answering Queries > (prove-simple 'parent ' (donald nancy) nil) (NIL) ;沒有binding可以prove所問的 > (prove-simple 'child '(?x ?y) nil) (((#:?6 . NANCY) (#:?5 . DONALD) (?Y . #:?5) (?X . #:?6))) ;有binding可以促成所詢問的關係 Inference - Answering Queries (defun change-vars (r) (sublis (mapcar #'(lambda (v) (cons v (gensym "?"))) (vars-in r)) r)) ;把r中的變數都換成新產生的?xxxx (defun vars-in (expr) ;找expr中所有的變數 (if (atom expr) (if (var? expr) (list expr)) (union (vars-in (car expr)) (vars-in (cdr expr))))) Inference - Answering Queries (defun prove-and (clauses binds) (if (null clauses) ;若遞迴到clauses為null (list binds) ;直接return目前的binds (mapcan #‘ (lambda (b) (prove (car clauses) b)) ;第一個clause作prove (prove-and (cdr clauses) binds)))) ;後面剩下的clause作prove-and (defun prove-or (clauses binds) (mapcan #‘(lambda (c) (prove c binds)) ;對每一個clause都 clauses)) ;繼續做prove (defun prove-not (clause binds) ;clause只有一句 (unless (prove clause binds) ;繼續prove,找新的binds (list binds))) ;prove不出來就用舊的binds Inference - Answering Queries (defmacro with-answer (query &body body) (let ((binds (gensym))) ;產生一個新變數,假設是g1 ‘(dolist (,binds (prove ’,query)) ;將prove完的binding結果 (let ,(mapcar #’(lambda (v) ;依序給g1 ‘(,v (binding ',v ,binds))) (vars-in query)) ,@body)))) ;query: 想問的問題,如 (parent ?x ?y) ;body : 找出binding後想把它怎麼處理 ;,@會把body evaluate出來並且把所有elements拆解出來 > (prove '(parent ?x ?y)) (((?Y . DEBBIE) (?X . DONALD)) ((?Y . NANCY) (?X . DONALD))) Inference - Answering Queries (with-answer (p ?x ?y) (f ?x ?y)) is macroexpanded into: (dolist (#:g1 (prove ‘(p ?x ?y))) (let ((?x (binding ‘?x #:g1)) (?y (binding ‘?y #:g1))) (f ?x ?y))) > (with-answer (parent ?x ?y) (format t “~A is the parent of ~A. ~%” ?x ?y)) DONALD is the parent of NANCY. NIL Inference - Analysis If we do a (clrhash *rules*) and then define the following rules and facts, 定義一堆事實與規則到hash table *rule*中 (<- (parent donald nancy)) (<- (parent donald debbie)) (<- (male donald)) (<- (father ?x ?y) (and (parent ?x ?y) (male ?x))) (<- (= ?x ?x)) (<- (sibling ?x ?y) (and (parent ?z ?x) (parent ?z ?y) (not (= ?x ?y)))) Inference - Analysis 我們最後想做到: we will be able to make inferences like the following: > (with-answer (father ?x ?y) (format t "~A is the father of ~A. ~%” ?x ?y)) DONALD is the father of DEBBIE. DONALD is the father of NANCY. NIL > (with-answer (sibling ?x ?y) (format t "~A is the sibling of ~A. ~%” ?x ?y)) DEBBIE is the sibling of NANCY. NANCY is the sibling of DEBBIE. NIL
© Copyright 2026 Paperzz