Functional programming techniques o Currying o Continuations o Streams. Lazy evaluation Currying o Haskell B. Curry. o Second-order programming: o Functions that return other functions. o Example: A two-arguments function is transformed in a oneargument function that returns a one-argument function. (defun adder (x) #'(lambda (y) (+ x y))) (adder 2) #'(lambda (y) (+ 2 y)) (funcall (adder 2) 3) 5 (setf f (adder 2)) (funcall f 3) 5 ML programming fun adder x y = x + y adder: int int int adder 2: int int
Examples (defun member (x l) (when l (or (equal (first l) x) (member x (rest l))))) (defun member-c (x) #'(lambda (list) (labels ((look (l) (when l (or (equal (first l) x) (look (rest l)))))) (look list)))) (defun new-member (a l) (funcall (member-c a) l)) Examples (defun map-c (fn) #'(lambda (args) (labels ((aplica (l) (when l (cons (funcall fn (first l)) (aplica (rest l)))))) (aplica args)))) (defun new-map (fn args) (funcall (map-c fn) args))
Currying o Generalization: Transform a function with n = m + k parameters into a function with m parameters that returns a function with k parameters. (defun swapper (x y ls) (cond ((null ls) nil) ((equal (first ls) x) (cons y (swapper x y (rest ls)))) ((equal (first ls) y) (cons x (swapper x y (rest ls)))) (t (cons (first ls) (swapper x y (rest ls)))))) (defun swapper-c (x y) #'(lambda (ls) (labels ((look-up (ls) (cond ((null ls) nil) ((equal (first ls) x) (cons y (look-up (rest ls)))) ((equal (first ls) y) (cons x (look-up (rest ls)))) (t (cons (first ls) (look-up (rest ls))))))) (look-up ls)))) (defun swapper-nou (x y ls) (funcall (swapper-c x y) ls)) (setf pp (swapper-c a b)) (funcall pp (c a d b e)) (c b d a e)
Currying o Useful when one (o more) of the arguments of a function remains constant o Efficiency in recursive programming: the stack of functional calls is shorter o Lambda functions of only one argument without losing of representation power o Related to the partial evaluation theorem by Kleene (1952): o Given a computable function f of n variables f(x 1,x 2,...,x n ) and k (k<=n) values a 1,..., a k. It is possible to compute a new function f such that: f (x k+1, x k+2,..., x n ) = f(x 1,x 2,...,x n ) Currying o AI interest: to model partial functions, needed for the management of incomplete reasoning o Partial deduction (logic programming) o Specialization: (A B) C A (B C) o Knowledge-base specialization (Milord II): (A, α), (A B C, ρ) (B C, f ( α, ρ)) a b z... KB... α β ζ b z... KB'... α β ζ z... KB''... α β ζ
Continuations o Idea: Given a n-argument function, we add a new continuation argument. The result of the original function is given to the continuation. o (defun f (x) x) o (defun f(x,continuation) (funcall continuation x)) o Program transformation technique based on secondorder functions. New programs are more efficiently represented. o Scheme o SML/NJ, Haskell o Actors model o Multiprocessing, search o Objects containing the state of the computation Example I (no recursive) Without continuations (defun length2 (x) (list x (length x))) (defun print-length2 (list) (format t The length is: ~A (length2 list))) With continuations (defun length2-c (x -c-) (funcall -c- (list x (length x)))) (defun print-length2 (list) (length2-c list # (lambda (val) (format t The length is: ~A (length2 val)))))
Continuations (recursive functions) o Program transformation technique based on secondorder functions. New programs are more efficiently represented. o Idea: Given a one-argument function, we find a new two-argument tail-recursive function. The first argument is of the same type that the original one and, the second is a function called continuation. o It is easy to define a new tail-recursive function. o Problem: second-order function representation is expensive. o Benefits when continuations can be represented as simple data structures, for instance, lists. Continuations (recursive functions) o Suppose the following function: f(x) = if p then q else E where x can appear in p, q and E o f appears only one time in E, applied over a sub expression of x, f(s(x)) o In the non trivial case a, where p[a/x] is false, the computation returns r = f(s(a)), γ = λw.ew where Ew = E[f(s(x))/w] o The function γ is applied over r, γ(r)
Continuations o Equivalent function for f: f(x) = f-tr(x,id) f-tr(x,γ) = γ(f(x)) = if p then γ(q) else f-tr(s(x),λw.γew) [Field i Harrison, 1988] o The function γ is called a continuation, the pending computation to perform after the calculation of the recursive call. Example II (recursive) o Optimization of tail-recursive calls No tail-recursive (defun fact (n) (if (= n 0) 1 (* n (fact (- n 1))))) (defun fact-c (n -c-) (if (= n 0) (funcall -c- 1) (fact-c (- n 1) # (lambda (val) (funcall -c- (* n val)))))) (defun fact (n) (fact-c n # (lambda (val) val)))
Memoize (defun memo (fn &key (key #'first) (test #'eql) name) "Return a memo-function of fn." (let ((table (make-hash-table :test test))) (setf (get name :memo) table) #'(lambda (&rest args) (let ((k (funcall key args))) (multiple-value-bind (val found-p) (gethash k table) (if found-p val (setf (gethash k table) (apply fn args)))))))) (defun clear-memoize (fn-name) "Clear the hash table from a memo function." (let ((table (get fn-name :memo))) (when table (clrhash table)))) (defun memoize (fn-name &key (key #'first) (test #'eql)) "Replace fn-name's global definition with a memoized version." (clear-memoize fn-name) (setf (symbol-function fn-name) (memo (symbol-function fn-name) :name fn-name :key key :test test))) Example III o Continuation in a local function Σ n x (defun sum-iter (n) (let ((sum 0)) (loop (if (= n 0) (return sum)) (setq sum (+ sum n)) (setq n (- n 1))))) (defun sum-clean (n) (labels ((sum-loop (n sum) (if (= n 0) sum (sum-loop (- n 1) (+ sum n))))) (sum-loop n 0))) (defun sum-cont (n -c-) (labels ((sum-loop (n sum) (if (= n 0) (funcall -c- sum) (sum-loop (- n 1) (+ sum n))))) (sum-loop n 0)))
Coroutines (make-array '(4 2 3) :initial-contents '(((a b c) (1 2 3)) ((d e f) (3 1 2)) ((g h i) (2 3 1)) ((j k l) (0 0 0)))) array-dimension aref (defun write-matrix (M) (let ((heigth (array-dimension M 0)) (width (array-dimension M 1))) (labels ((write-loop (i j corou) (cond ((= i heigth) nil) ((= j width) (write-loop (1+ i) 0 corou)) (t (funcall corou #'(lambda (item corou) (setf (aref M i j) item) (write-loop i (1+ j) corou))))))) (write-loop 0 0 corou))))) (defun read-matrix (M) (let ((heigth (array-dimension M 0)) (width (array-dimension M 1))) (labels ((read-loop (i j corou) (cond ((= i heigth) nil) ((= j width) (read-loop (1+ i) 0 corou)) (t (funcall corou (aref M i j) (read-loop i (1+ j) corou))))))) (read-loop 0 0 corou))))) (defun transfer-matrix (M1 M2) (let ((reader (read-matrix M1)) (writer (write-matrix M2))) (funcall writer reader))) Coroutines M1 = #2A((1 2 3)(4 5 6)) M2 = #2A((nil nil)(nil nil)) (funcall (write-matrix M2)(read-matrix M1)) M2 = #2A((1 2)(3 4)) (funcall (labels ((write-loop (i j corou) (cond ((= i 2) nil) ((= j 2) (write-loop (1+ i) 0 corou)) (t (funcall corou #'(lambda (item corou) (setf (aref M2 i j) item) (write-loop i (1+ j) corou))))))) (write-loop 0 0 corou))) (labels ((read-loop (i j corou) (cond ((= i 2) nil) ((= j 3) (read-loop (1+ i) 0 corou)) (t (funcall corou (aref M1 i j) (read-loop i (1+ j) corou))))))) (read-loop 0 0 corou)))) (funcall (labels ((read-loop (i j corou) (cond ((= i 2) nil) ((= j 3) (read-loop (1+ i) 0 corou)) (t (funcall corou (aref M1 i j) (read-loop i (1+ j) corou))))))) (read-loop 0 0 corou))) #'(lambda (item corou) (setf (aref M2 0 0) item) (write-loop 0 1 corou))) (funcall #'(lambda (item corou) (setf (aref M2 0 0) item) (write-loop 0 1 corou)) (aref M1 0 0) (read-loop 0 1 corou))) (setf (aref M2 0 0) (aref M1 0 0)) (write-loop 0 1 (read-loop 0 1 corou))))
Control Flow o Continuations are useful to define control strategies o Common Lisp uses continuations in: o BLOCK/RETURN_FROM lexical o CATCH/THROW dynamic (catch tag (...) (...)...) (if (...) (block then (setq...) (list... (return-from then...))) (...) (defun f (x) (catch 'hola (+ x 1) (+ 5 (g x)))) (throw tag (...)) (defun g (x) (if (< x 5) 567 (throw 'hola 10000))) Control Flow OnLisp: Chapter 20 o These mechanisms can be described with the function CALL-WITH-CURRENT-CONTINUATION (no Common Lisp) o Scheme (lisp dialect) o (+ 1 call/cc (lambda (cont) (+ 20 300))) 321 o (+ 1 call/cc (lambda (cont) (+ 20 (cont 300)))) 301 (defmacro block (name &rest forms) `(call-with-current-continuation #'(lambda (,name),@forms))) (defmacro return-from (name &optional value) `(funcall,name,value))
ATN (Augmented transition network) s::= np vp to-be np pred np::= proper-noun pronoun det noun vp::= to-be np to-be pred pred::= adj pp pp::= prep np adj::= nice small blue prep::= to in on out to-be ::= is are was were det::= the proper-noun::= maria carles antoni noun::= house car table pronoun::= mine yours ours theirs Maria is nice The house is blue The table is mine Two continuations: success or failure ATN (Augmented transition network) I (defun cont-atn (s) (parse-s s (cond ((null s) success) (T (funcall return)))) 'failure))) (defun parse-s (s success failure) (parse-np s (parse-vp s success return)) (categoria? 'to-be s (parse-np s (parse-pred s sucess return)) return)) failure)))) Success Failure (defun parse-np (s success failure) (categoria? 'proper-noun s sucess (categoria? 'pronoun s sucess (categoria? 'det s (categoria? 'noun s sucess return)) failure)))))) Syntax? (cont-atn (Maria is nice)) (parse-s (Maria is nice) success1 failure1) (parse-np (Maria is nice success2 failure2) (categoria? proper-noun (Maria is nice) success2 failure3)
ATN II (defun parse-pred (s success failure) (categoria? 'adj s success (parse-pp s success failure)))) (defun parse-pp (s success failure) (categoria? 'prep s (parse-np s success return)) failure)) (defun categoria? (caract s success failure) (if (or (null s) (not (categoriap (first s) caract))) (funcall failure) (funcall success (rest s) failure))) (defun categoriap (mot tipus) (case tipus (adj (member mot '(nice small blue))) (prep (member mot '(to in on out))) (to-be (member mot '(is are was were))) (det (member mot '(the))) (proper-noun (member mot '(maria carles antoni))) (noun (member mot '(house car table))) (pronoun (member mot '(mine yours ours theirs))))) (categoria? proper-noun (Maria is nice) success2 failure3) (funcall success2 (is nice) failure3) (funcall # (lambda(s return) (parse-vp s success1 return)) (is nice) failure3) (parse-vp (is nice) success1 failure3) (categoria? to-be (is nice) success1 failure3) (funcall # (lambda(s return) (parse-np s success1 return)) (nice) failure3) ATN III (defun parse-np (s success failure) (categoria? 'proper-noun s success (categoria? 'pronoun s success (categoria? 'det s (categoria? 'noun s success return)) failure)))))) (defun parse-vp (s success failure) (categoria? 'to-be s (parse-np s success return)) (categoria? 'to-be s (parse-pred s success return)) failure))))
ATN IV (categoria? proper-noun (The house is mine) success2 failure3) (funcall failure3) (funcall (categoria? 'pronoun (The house is mine) success2 (categoria? 'det s ) (funcall (categoria? 'det (The house is mine) (categoria? 'noun s success2 return)) failure2))))))) (categoria? to-be (is mine) success1 failure3) IA interests o Pros o Search and backtracking can be simplified. o Interesting control mechanisms. Necessary to define languages. o Modular and compact programming of ATNs o No backtracking o Continuation over failure states. o Cons o Read and write this kind of code is not easy. o Problematic program debugging with anonymous functions o Inspiration