CS476 / MCS415 Fall 2005 -- LISP HANDOUT http://mypage.iu.edu/~colallen/lp/: nice online LISP tutorial http://www.paulgraham.com/lisp.html: lots of interesting information about lisp, including: "What made LISP so different", a historical account of what mcCarthy invented; "A Lisp Startup", an article on Viaweb Store: "It describes how we used Lisp to write Viaweb Store, which is still, as Yahoo! Store, the most popular e-commerce software, running about 14,000 stores. " Also see "Applications", under "Lisp Links" Lisp Executable on oscar.cs.uic.edu: /usr/local/acl70/alisp barbara [1]>/usr/local/acl70/alisp International Allegro CL Enterprise Edition 7.0 [Linux (x86)] (Oct 19, 2004 13:27) Copyright (C) 1985-2004, Franz Inc., Berkeley, CA, USA. All Rights Reserved. This development copy of Allegro CL is licensed to: [TC10974] University of Illinois at Chicago ;; Optimization settings: safety 1, space 1, speed 1, debug 2. ;; For a complete description of all compiler switches given the ;; current optimization settings evaluate (EXPLAIN-COMPILER-SETTINGS). ; Functions CL-USER(1): (+ 10 6) 16 CL-USER(3): (- 10 6) 4 CL-USER(4): (+ 3 (- 10 6) 4) 11 ; quotes CL-USER(5): '(a b c) (A B C) ; Lisp is not case sensitive (at least not this version) CL-USER(6): '(Aa bb CC) (AA BB CC) ;;; some easy function definitions ; use 'defun' to define functions CL-USER(34): (defun easyf (a b c) (+ (* a b) c)) EASYF CL-USER(35): (easyf 3 5 17) 32 CL-USER(7): (defun sum-it(list) (apply #'+ (remove nil list))) SUM-IT CL-USER(8): (sum-it '(3 4 6 nil nil nil)) 13 ; the following definitions plus the error that follows ; show that functions don't change their arguments, ; ie, 'remove' leaves its argument unchanged. CL-USER(9): (defun wrong-sum-it (list) (remove nil list) (apply #'+ list)) WRONG-SUM-IT CL-USER(10): (wrong-sum-it '(3 4 6 nil nil nil)) Error: `NIL' is not of the expected type `NUMBER' [condition type: TYPE-ERROR] Restart actions (select using :continue): 0: Return to Top Level (an "abort" restart). 1: Abort entirely from this process. ; to return from a debugging loop you can use "res" [1] CL-USER(11): :res ; nil, t, etc CL-USER(12): NIL NIL CL-USER(13): T T CL-USER(14): t T CL-USER(15): () NIL ;;; How to build lists ; cons CL-USER(16): (cons 'a 'b) (A . B) CL-USER(17): (cons 'a nil) (A) CL-USER(18): (cons 'a '(b c)) (A B C) ; list and append CL-USER(26): (list 'a 'b) (A B) CL-USER(27): (append '(1 2 3) '(a b c) '(pasta)) (1 2 3 A B C PASTA) CL-USER(29): (append '(1 2 3) '(a b c) 'pasta) (1 2 3 A B C . PASTA) CL-USER(7): (append 'pasta '(1 2 3)) Error: `PASTA' is not of the expected type `LIST' [condition type: TYPE-ERROR] ; how to access elements of a list CL-USER(19): (car '( a b c)) A CL-USER(20): (car '((a) b c)) (A) CL-USER(21): (caddr '(a b c)) C CL-USER(22): (third '(a b c)) C CL-USER(23): (cadddr '(a b c d e f g)) D CL-USER(24): (seventh '(a b c d e f g)) G ;;; control flow CL-USER(31): (if (listp 'a) (+ 5 7) (- 5 3)) 2 CL-USER(32): (if (atom 'a) (+ 5 7) (- 5 3)) 12 CL-USER(35): (cond ((listp '(a b c)) (format t "my name is ~A~%" 'barbara) (* 2 3)) ((numberp 9) (format t "and my last name is ~A~%" "di eugenio") (- 10 7)) (t (+ 10 7))) my name is BARBARA 6 CL-USER(36): (cond ((listp 'a) (format t "my name is ~A~%" 'barbara) (* 2 3)) ((numberp 9) (format t "and my last name is ~A~%" "di eugenio") (- 10 7)) (t (+ 10 7))) and my last name is di eugenio 3 CL-USER(37): (cond ((listp 'a) (format t "my name is ~A~%" 'barbara) (* 2 3)) ((numberp 'a) (format t "and my last name is ~A~%" "di eugenio") (- 10 7)) (t (+ 10 7))) 17 ; equality CL-USER(40): (eql (list 'a) (list 'a)) NIL CL-USER(41): (equal (list 'a) (list 'a)) T ; membership in a list: also shows how to use ; optional params in functions CL-USER(56): (member 3 '(4 3 5)) (3 5) CL-USER(57): (member 6 '(4 3 5)) NIL CL-USER(58): (member 'a '((a)) :key 'car) ((A)) CL-USER(59): (member-if 'oddp '(4 6 7)) (7) ;;; loading files ; The function clean-list demonstrates how to recursively traverse lists ; and build a result which is a list CL-USER(30): (load "clean-list") ; Loading /home2/fac2/bdieugen/clean-list.lisp T CL-USER(31): (clean-list '( 1 2 3 3 3 2 2 1 2)) (3 1 2) CL-USER(19): (clean-list '(a a b b a c a c c)) (B A C) CL-USER(32): (clean-list 'g) not a list NIL CL-USER(39): (documentation 'clean-list 'function) "checks that it has a list, and calls clean-list1" ;tracing functions CL-USER(38): (trace clean-list1) (CLEAN-LIST1) CL-USER(41): (clean-list '( a b c c b a)) 0[1]: (CLEAN-LIST1 (A B C C B A)) 1[1]: (CLEAN-LIST1 (B C C B A)) 2[1]: (CLEAN-LIST1 (C C B A)) 3[1]: (CLEAN-LIST1 (C B A)) 4[1]: (CLEAN-LIST1 (B A)) 5[1]: (CLEAN-LIST1 (A)) 6[1]: (CLEAN-LIST1 NIL) 6[1]: returned NIL 5[1]: returned (A) 4[1]: returned (B A) 3[1]: returned (C B A) 2[1]: returned (C B A) 1[1]: returned (C B A) 0[1]: returned (C B A) (C B A) CL-USER(42): (untrace clean-list1) (CLEAN-LIST1) ;; a more complex function CL-USER(1): (load "compress.lisp") ; Loading /home2/fac2/bdieugen/compress.lisp T CL-USER(2): (compress '(1 1 1 d d u f f 2 2 at at)) ((3 1) (2 D) U (2 F) (2 2) (2 AT)) CL-USER(7): (trace compr) (COMPR) CL-USER(8): (compress '(1 1 1 d d u f f)) 0[1]: (COMPRESS (1 1 1 D D U F F)) 1[1]: (COMPR 1 1 (1 1 D D U F F)) 2[1]: (COMPR 1 2 (1 D D U F F)) 3[1]: (COMPR 1 3 (D D U F F)) 4[1]: (COMPR D 1 (D U F F)) 5[1]: (COMPR D 2 (U F F)) 6[1]: (COMPR U 1 (F F)) 7[1]: (COMPR F 1 (F)) 8[1]: (COMPR F 2 NIL) 8[1]: returned ((2 F)) 7[1]: returned ((2 F)) 6[1]: returned (U (2 F)) 5[1]: returned ((2 D) U (2 F)) 4[1]: returned ((2 D) U (2 F)) 3[1]: returned ((3 1) (2 D) U (2 F)) 2[1]: returned ((3 1) (2 D) U (2 F)) 1[1]: returned ((3 1) (2 D) U (2 F)) 0[1]: returned ((3 1) (2 D) U (2 F)) ((3 1) (2 D) U (2 F)) ; compiling files CL-USER(9): (compile-file "compress.lisp") ;;; Compiling file compress.lisp ;;; Writing fasl file compress.fasl Warning: No IN-PACKAGE form seen in /home2/fac2/bdieugen/compress.lisp. (Allegro Presto will be ineffective when loading a file having no IN-PACKAGE form.) ;;; Fasl write complete #p"compress.fasl" T NIL CL-USER(10): (load "compress") ; Fast loading /home2/fac2/bdieugen/compress.fasl T CL-USER(12): (untrace compr) (COMPR) CL-USER(15): (compress '(1 1 1 1 1 1 1 1 1 1 )) ((10 1)) ; I/O: input / output files, you need to open a stream ; (see Input / Output in online tutorial) CL-USER(21): (format 't "~A * ~A equals ~A~%" 6 7 (* 6 7)) 6 * 7 equals 42 NIL CL-USER(26): (princ "It's rainy today") It's rainy today "It's rainy today" CL-USER(27): (prin1 "It's rainy today") "It's rainy today" "It's rainy today" ; mapcar, maplist CL-USER(36): (mapcar #'squares '(3 10) '(7 13)) 3 squared is 9 4 squared is 16 5 squared is 25 6 squared is 36 7 squared is 49 10 squared is 100 11 squared is 121 12 squared is 144 13 squared is 169 (DONE DONE) CL-USER(37): (maplist #'(lambda (x) (format t "I am working on ~A~%" x) (listp x)) '(a b c)) I am working on (A B C) I am working on (B C) I am working on (C) (T T T) ; apply and funcall allow to write more sophisticated functions CL-USER(1): (apply #'list '(a b c)) (A B C) CL-USER(2): (apply #'* '(2 3 5)) 30 CL-USER(3): (* 2 3 5) 30 CL-USER(4): (funcall #'* 2 3 5) 30 CL-USER(5): (defun combiner (x) (cond ((numberp x) #'*) ((listp x) #'append) ((atom x) #'list) ) ) COMBINER CL-USER(6): (combiner 3) # CL-USER(7): (combiner '(a (b c))) # CL-USER(8): (combiner 'a) # CL-USER(12): (defun combine (&rest args) (apply (combiner (car args)) args)) COMBINE CL-USER(13): (combine 4 3 5 7) 420 CL-USER(14): (combine '(a c) '(f g) '(s t (d f))) (A C F G S T (D F)) CL-USER(15): (combine 'a 'b 'c) (A B C) CL-USER(72): (combine '(a . b) '(c . d)) (A C . D) CL-USER(74): (combine '(a . b) '(c . d) '(e . f)) (A C E . F) ;;; Some imperative programming flavor ; assignment CL-USER(16): (setf g '( t u r V)) (T U R V) CL-USER(17): g (T U R V) CL-USER(18): 'g G CL-USER(19): (remove 't g) (U R V) CL-USER(20): g (T U R V) CL-USER(33): (clean-list g) (T U R V) ; iteration -- just for completeness, in functional programming ; one should use recursion as much as possible CL-USER(34): (defun squares (begin end) (do ((i begin (+ i 1))) ((> i end) 'done) (format t "~A squared is ~A~%" i (* i i)))) SQUARES CL-USER(35): (squares 3 8) 3 squared is 9 4 squared is 16 5 squared is 25 6 squared is 36 7 squared is 49 8 squared is 64 DONE ; but sometimes iteration is more efficient ... CL-USER(44): (defun factorial (n) (do ((j n (- j 1)) (f 1 (* j f))) ((= j 0) f))) CL-USER(50): (factorial 5) 120 CL-USER(51): (fboundp 'factorial) # CL-USER(52): (fboundp factorial) Error: Attempt to take the value of the unbound variable `FACTORIAL'. [condition type: UNBOUND-VARIABLE] ;;; static or dynamic scope? CL-USER(30): (let ((x 10)) (defun foo () x)) FOO CL-USER(31): (let ((x 20)) (foo)) 10 CL-USER(32): (let ((x 10)) (defun foo () (declare (special x)) x)) FOO CL-USER(35): (setf x 50) 50 CL-USER(36): (foo) 50 ;;; macros CL-USER(37): (defmacro nil! (x) (list 'setf x nil)) NIL! CL-USER(38): (nil! a) NIL CL-USER(43): (defmacro nil2! (x) `(setf ,x nil)) NIL2! CL-USER(46): (nil2! a) NIL ;;; Closures CL-USER(17): (defun make-adder (n) #'(lambda (x) (+ x n))) MAKE-ADDER CL-USER(18): (setf add3 (make-adder 3)) # CL-USER(19): (funcall add3 5) 8 CL-USER(23): (apply add3 '(5)) 8 CL-USER(24): (setf add25 (make-adder 25)) # CL-USER(25): (apply add25 '(5)) 30 CL-USER(29): (mapcar add25 '(5 7 10)) (30 32 35)