diff options
author | Rommel Martinez <ebzzry@ebzzry.io> | 2018-03-01 21:25:04 +0800 |
---|---|---|
committer | Rommel Martinez <ebzzry@ebzzry.io> | 2018-03-01 21:25:04 +0800 |
commit | 6968c5724892ec0ea787929a307552d3dd211974 (patch) | |
tree | 65ba666aaea987215952d88a56517deb7e770701 | |
parent | 9f33f260dc305a40b6476c081ea16e75ac2ced50 (diff) |
[common-lisp/en]: clean up and add more information
-rw-r--r-- | common-lisp.html.markdown | 662 |
1 files changed, 360 insertions, 302 deletions
diff --git a/common-lisp.html.markdown b/common-lisp.html.markdown index 9a23bc26..5f6975b6 100644 --- a/common-lisp.html.markdown +++ b/common-lisp.html.markdown @@ -4,82 +4,91 @@ language: "Common Lisp" filename: commonlisp.lisp contributors: - ["Paul Nathan", "https://github.com/pnathan"] + - ["Rommel Martinez", "https://ebzzry.io"] --- -ANSI Common Lisp is a general purpose, multi-paradigm programming -language suited for a wide variety of industry applications. It is -frequently referred to as a programmable programming language. +Common Lisp is a general-purpose, multi-paradigm programming language suited for a wide variety of +industry applications. It is frequently referred to as a programmable programming language. -The classic starting point is [Practical Common Lisp and freely available.](http://www.gigamonkeys.com/book/) - -Another popular and recent book is -[Land of Lisp](http://landoflisp.com/). +The classic starting point is [Practical Common Lisp](http://www.gigamonkeys.com/book/). Another +popular and recent book is [Land of Lisp](http://landoflisp.com/). A new book about best practices, +[Common Lisp Recipes](http://weitz.de/cl-recipes/), was recently published. ```common_lisp -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;----------------------------------------------------------------------------- ;;; 0. Syntax -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;----------------------------------------------------------------------------- -;;; General form. +;;; General form -;; Lisp has two fundamental pieces of syntax: the ATOM and the -;; S-expression. Typically, grouped S-expressions are called `forms`. +;;; CL has two fundamental pieces of syntax: ATOM and S-EXPRESSION. +;;; Typically, grouped S-expressions are called `forms`. -10 ; an atom; it evaluates to itself +10 ; an atom; it evaluates to itself +:thing ; another atom; evaluating to the symbol :thing +t ; another atom, denoting true +(+ 1 2 3 4) ; an s-expression +'(4 :foo t) ; another s-expression -:THING ;Another atom; evaluating to the symbol :thing. -t ; another atom, denoting true. +;;; Comments -(+ 1 2 3 4) ; an s-expression +;;; Single-line comments start with a semicolon; use four for file-level +;;; comments, three for section descriptions, two inside definitions, and one +;;; for single lines. For example, -'(4 :foo t) ;another one +;;;; life.lisp +;;; Foo bar baz, because quu quux. Optimized for maximum krakaboom and umph. +;;; Needed by the function LINULUKO. -;;; Comments +(defun meaning (life) + "Return the computed meaning of LIFE" + (let ((meh "abc")) + ;; Invoke krakaboom + (loop :for x :across meh + :collect x))) ; store values into x, then return it -;; Single line comments start with a semicolon; use two for normal -;; comments, three for section comments, and four for file-level -;; comments. +;;; Block comments, on the other hand, allow for free-form comments. They are +;;; delimited with #| and |# -#| Block comments - can span multiple lines and... +#| This is a block comment which + can span multiple lines and #| they can be nested! |# |# -;;; Environment. -;; A variety of implementations exist; most are -;; standard-conformant. CLISP is a good starting one. +;;; Environment -;; Libraries are managed through Quicklisp.org's Quicklisp system. +;;; A variety of implementations exist; most are standards-conformant. SBCL +;;; is a good starting point. Third party libraries can be easily installed with +;;; Quicklisp -;; Common Lisp is usually developed with a text editor and a REPL -;; (Read Evaluate Print Loop) running at the same time. The REPL -;; allows for interactive exploration of the program as it is "live" -;; in the system. +;;; CL is usually developed with a text editor and a Real Eval Print +;;; Loop (REPL) running at the same time. The REPL allows for interactive +;;; exploration of the program while it is running "live". -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; 1. Primitive Datatypes and Operators -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;----------------------------------------------------------------------------- +;;; 1. Primitive datatypes and operators +;;;----------------------------------------------------------------------------- ;;; Symbols 'foo ; => FOO Notice that the symbol is upper-cased automatically. -;; Intern manually creates a symbol from a string. +;;; INTERN manually creates a symbol from a string. -(intern "AAAA") ; => AAAA - -(intern "aaa") ; => |aaa| +(intern "AAAA") ; => AAAA +(intern "aaa") ; => |aaa| ;;; Numbers + 9999999999999999999999 ; integers #b111 ; binary => 7 #o111 ; octal => 73 @@ -89,313 +98,362 @@ t ; another atom, denoting true. 1/2 ; ratios #C(1 2) ; complex numbers +;;; Function application are written as (f x y z ...) where f is a function and +;;; x, y, z, ... are the arguments. + +(+ 1 2) ; => 3 + +;;; If you want to create literal data, use QUOTE to prevent it from being +;;; evaluated + +(quote (+ 1 2)) ; => (+ 1 2) +(quote a) ; => A + +;;; The shorthand for QUOTE is ' + +'(+ 1 2) ; => (+ 1 2) +'a ; => A + +;;; Basic arithmetic operations + +(+ 1 1) ; => 2 +(- 8 1) ; => 7 +(* 10 2) ; => 20 +(expt 2 3) ; => 8 +(mod 5 2) ; => 1 +(/ 35 5) ; => 7 +(/ 1 3) ; => 1/3 +(+ #C(1 2) #C(6 -4)) ; => #C(7 -2) + +;;; Booleans + +t ; true; any non-NIL value is true +nil ; false; also, the empty list: () +(not nil) ; => T +(and 0 t) ; => T +(or 0 nil) ; => 0 + +;;; Characters + +#\A ; => #\A +#\λ ; => #\GREEK_SMALL_LETTER_LAMDA +#\u03BB ; => #\GREEK_SMALL_LETTER_LAMDA + +;;; Strings are fixed-length arrays of characters -;; Function application is written (f x y z ...) -;; where f is a function and x, y, z, ... are operands -;; If you want to create a literal list of data, use ' to stop it from -;; being evaluated - literally, "quote" the data. -'(+ 1 2) ; => (+ 1 2) -;; You can also call a function manually: -(funcall #'+ 1 2 3) ; => 6 -;; Some arithmetic operations -(+ 1 1) ; => 2 -(- 8 1) ; => 7 -(* 10 2) ; => 20 -(expt 2 3) ; => 8 -(mod 5 2) ; => 1 -(/ 35 5) ; => 7 -(/ 1 3) ; => 1/3 -(+ #C(1 2) #C(6 -4)) ; => #C(7 -2) - - ;;; Booleans -t ; for true (any not-nil value is true) -nil ; for false - and the empty list -(not nil) ; => t -(and 0 t) ; => t -(or 0 nil) ; => 0 - - ;;; Characters -#\A ; => #\A -#\λ ; => #\GREEK_SMALL_LETTER_LAMDA -#\u03BB ; => #\GREEK_SMALL_LETTER_LAMDA - -;;; Strings are fixed-length arrays of characters. "Hello, world!" "Benjamin \"Bugsy\" Siegel" ; backslash is an escaping character -;; Strings can be concatenated too! -(concatenate 'string "Hello " "world!") ; => "Hello world!" +;;; Strings can be concatenated + +(concatenate 'string "Hello, " "world!") ; => "Hello, world!" + +;;; A string can be treated like a sequence of characters -;; A string can be treated like a sequence of characters (elt "Apple" 0) ; => #\A -;; format can be used to format strings: -(format nil "~a can be ~a" "strings" "formatted") +;;; FORMAT is used to create formatted output, which ranges from simple string +;;; interpolation to loops and conditionals. The first argument to FORMAT +;;; determines where will the formatted string go. If it is NIL, FORMAT +;;; simply returns the formatted string as a value; if it is T, FORMAT outputs +;;; to the standard output, usually the screen, then it returns NIL. + +(format nil "~A, ~A!" "Hello" "world") ; => "Hello, world!" +(format t "~A, ~A!" "Hello" "world") ; => NIL -;; Printing is pretty easy; ~% is the format specifier for newline. -(format t "Common Lisp is groovy. Dude.~%") +;;;----------------------------------------------------------------------------- +;;; 2. Variables +;;;----------------------------------------------------------------------------- -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 2. Variables -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; You can create a global (dynamically scoped) using defparameter -;; a variable name can use any character except: ()",'`;#|\ +;;; You can create a global (dynamically scoped) variable using DEFVAR and +;;; DEFPARAMETER. The variable name can use any character except: ()",'`;#|\ -;; Dynamically scoped variables should have earmuffs in their name! +;;; The difference between DEFVAR and DEFPARAMETER is that re-evaluating a +;;; DEFVAR expression doesn't change the value of the variable. DEFPARAMETER, +;;; on the other hand, does. + +;;; By convention, dynamically scoped variables have earmuffs in their name. (defparameter *some-var* 5) *some-var* ; => 5 -;; You can also use unicode characters. +;;; You can also use unicode characters. (defparameter *AΛB* nil) +;;; Accessing a previously unbound variable is an undefined behavior, but +;;; possible. Don't do it. + +;;; You can create local bindings with LET. In the following snippet, `me` is +;;; bound to "dance with you" only within the (let ...). LET always returns +;;; the value of the last `form` in the LET form. -;; Accessing a previously unbound variable is an -;; undefined behavior (but possible). Don't do it. +(let ((me "dance with you")) me) ; => "dance with you" -;; Local binding: `me` is bound to "dance with you" only within the -;; (let ...). Let always returns the value of the last `form` in the -;; let form. +;;;-----------------------------------------------------------------------------; +;;; 3. Structs and collections +;;;-----------------------------------------------------------------------------; -(let ((me "dance with you")) - me) -;; => "dance with you" -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 3. Structs and Collections -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Structs -;; Structs (defstruct dog name breed age) (defparameter *rover* (make-dog :name "rover" :breed "collie" :age 5)) -*rover* ; => #S(DOG :NAME "rover" :BREED "collie" :AGE 5) - -(dog-p *rover*) ; => true #| -p signifies "predicate". It's used to - check if *rover* is an instance of dog. |# +*rover* ; => #S(DOG :NAME "rover" :BREED "collie" :AGE 5) +(dog-p *rover*) ; => T (dog-name *rover*) ; => "rover" -;; Dog-p, make-dog, and dog-name are all created by defstruct! +;;; DOG-P, MAKE-DOG, and DOG-NAME are all automatically created by DEFSTRUCT + ;;; Pairs -;; `cons' constructs pairs, `car' and `cdr' extract the first -;; and second elements -(cons 'SUBJECT 'VERB) ; => '(SUBJECT . VERB) -(car (cons 'SUBJECT 'VERB)) ; => SUBJECT -(cdr (cons 'SUBJECT 'VERB)) ; => VERB + +;;; CONS constructs pairs. CAR and CDR return the head and tail of a CONS-pair. + +(cons 'SUBJECT 'VERB) ; => '(SUBJECT . VERB) +(car (cons 'SUBJECT 'VERB)) ; => SUBJECT +(cdr (cons 'SUBJECT 'VERB)) ; => VERB + ;;; Lists -;; Lists are linked-list data structures, made of `cons' pairs and end -;; with a `nil' (or '()) to mark the end of the list -(cons 1 (cons 2 (cons 3 nil))) ; => '(1 2 3) -;; `list' is a convenience variadic constructor for lists -(list 1 2 3) ; => '(1 2 3) -;; and a quote can also be used for a literal list value -'(1 2 3) ; => '(1 2 3) +;;; Lists are linked-list data structures, made of CONS pairs and end with a +;;; NIL (or '()) to mark the end of the list + +(cons 1 (cons 2 (cons 3 nil))) ; => '(1 2 3) + +;;; LIST is a convenience variadic constructor for lists + +(list 1 2 3) ; => '(1 2 3) -;; Can still use `cons' to add an item to the beginning of a list -(cons 4 '(1 2 3)) ; => '(4 1 2 3) +;;; When the first argument to CONS is an atom and the second argument is a +;;; list, CONS returns a new CONS-pair with the first argument as the first +;;; item and the second argument as the rest of the CONS-pair -;; Use `append' to - surprisingly - append lists together -(append '(1 2) '(3 4)) ; => '(1 2 3 4) +(cons 4 '(1 2 3)) ; => '(4 1 2 3) -;; Or use concatenate - +;;; Use APPEND to join lists -(concatenate 'list '(1 2) '(3 4)) +(append '(1 2) '(3 4)) ; => '(1 2 3 4) + +;;; Or CONCATENATE + +(concatenate 'list '(1 2) '(3 4)) ; => '(1 2 3 4) + +;;; Lists are a very central type, so there is a wide variety of functionality for +;;; them, a few examples: -;; Lists are a very central type, so there is a wide variety of functionality for -;; them, a few examples: (mapcar #'1+ '(1 2 3)) ; => '(2 3 4) (mapcar #'+ '(1 2 3) '(10 20 30)) ; => '(11 22 33) (remove-if-not #'evenp '(1 2 3 4)) ; => '(2 4) -(every #'evenp '(1 2 3 4)) ; => nil +(every #'evenp '(1 2 3 4)) ; => NIL (some #'oddp '(1 2 3 4)) ; => T (butlast '(subject verb object)) ; => (SUBJECT VERB) ;;; Vectors -;; Vector's literals are fixed-length arrays -#(1 2 3) ; => #(1 2 3) - -;; Use concatenate to add vectors together -(concatenate 'vector #(1 2 3) #(4 5 6)) ; => #(1 2 3 4 5 6) +;;; Vector's literals are fixed-length arrays -;;; Arrays +#(1 2 3) ; => #(1 2 3) -;; Both vectors and strings are special-cases of arrays. +;;; Use CONCATENATE to add vectors together -;; 2D arrays +(concatenate 'vector #(1 2 3) #(4 5 6)) ; => #(1 2 3 4 5 6) -(make-array (list 2 2)) -;; (make-array '(2 2)) works as well. +;;; Arrays -; => #2A((0 0) (0 0)) +;;; Both vectors and strings are special-cases of arrays. -(make-array (list 2 2 2)) +;;; 2D arrays -; => #3A(((0 0) (0 0)) ((0 0) (0 0))) +(make-array (list 2 2)) ; => #2A((0 0) (0 0)) +(make-array '(2 2)) ; => #2A((0 0) (0 0)) +(make-array (list 2 2 2)) ; => #3A(((0 0) (0 0)) ((0 0) (0 0))) -;; Caution- the default initial values are -;; implementation-defined. Here's how to define them: +;;; Caution: the default initial values of MAKE-ARRAY are implementation-defined. +;;; To explicitly specify them: -(make-array '(2) :initial-element 'unset) +(make-array '(2) :initial-element 'unset) ; => #(UNSET UNSET) -; => #(UNSET UNSET) +;;; To access the element at 1, 1, 1: -;; And, to access the element at 1,1,1 - -(aref (make-array (list 2 2 2)) 1 1 1) +(aref (make-array (list 2 2 2)) 1 1 1) ; => 0 -; => 0 ;;; Adjustable vectors -;; Adjustable vectors have the same printed representation -;; as fixed-length vector's literals. +;;; Adjustable vectors have the same printed representation as +;;; fixed-length vector's literals. (defparameter *adjvec* (make-array '(3) :initial-contents '(1 2 3) - :adjustable t :fill-pointer t)) - + :adjustable t :fill-pointer t)) *adjvec* ; => #(1 2 3) -;; Adding new element: -(vector-push-extend 4 *adjvec*) ; => 3 - -*adjvec* ; => #(1 2 3 4) +;;; Adding new elements +(vector-push-extend 4 *adjvec*) ; => 3 +*adjvec* ; => #(1 2 3 4) -;;; Naively, sets are just lists: +;;; Sets, naively, are just lists: -(set-difference '(1 2 3 4) '(4 5 6 7)) ; => (3 2 1) -(intersection '(1 2 3 4) '(4 5 6 7)) ; => 4 -(union '(1 2 3 4) '(4 5 6 7)) ; => (3 2 1 4 5 6 7) -(adjoin 4 '(1 2 3 4)) ; => (1 2 3 4) +(set-difference '(1 2 3 4) '(4 5 6 7)) ; => (3 2 1) +(intersection '(1 2 3 4) '(4 5 6 7)) ; => 4 +(union '(1 2 3 4) '(4 5 6 7)) ; => (3 2 1 4 5 6 7) +(adjoin 4 '(1 2 3 4)) ; => (1 2 3 4) -;; But you'll want to use a better data structure than a linked list -;; for performant work! +;;; However, you'll need a better data structure than linked lists when working +;;; with larger data sets ;;; Dictionaries are implemented as hash tables. -;; Create a hash table +;;; Create a hash table + (defparameter *m* (make-hash-table)) -;; set a value +;;; Set value + (setf (gethash 'a *m*) 1) -;; Retrieve a value -(gethash 'a *m*) ; => 1, t +;;; Retrieve value + +(gethash 'a *m*) ; => 1, T + +;;; CL expressions have the ability to return multiple values. + +(values 1 2) ; => 1, 2 + +;;; which can be bound with MULTIPLE-VALUE-BIND + +(multiple-value-bind (x y) + (values 1 2) + (list y x)) + +; => '(2 1) -;; Detail - Common Lisp has multiple return values possible. gethash -;; returns t in the second value if anything was found, and nil if -;; not. +;;; GETHASH is an example of a function that returns multiple values. The first +;;; value it return is the value of the key in the hash table; if the key is +;;; not found it returns NIL. -;; Retrieving a non-present value returns nil - (gethash 'd *m*) ;=> nil, nil +;;; The second value determines if that key is indeed present in the hash +;;; table. If a key is not found in the table it returns NIL. This behavior +;;; allows us to check if the value of a key is actually NIL. + +;;; Retrieving a non-present value returns nil + +(gethash 'd *m*) ;=> NIL, NIL + +;;; You can provide a default value for missing keys -;; You can provide a default value for missing keys (gethash 'd *m* :not-found) ; => :NOT-FOUND -;; Let's handle the multiple return values here in code. +;;; Let's handle the multiple return values here in code. -(multiple-value-bind - (a b) +(multiple-value-bind (a b) (gethash 'd *m*) (list a b)) ; => (NIL NIL) -(multiple-value-bind - (a b) +(multiple-value-bind (a b) (gethash 'a *m*) (list a b)) ; => (1 T) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 3. Functions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Use `lambda' to create anonymous functions. -;; A function always returns the value of its last expression. -;; The exact printable representation of a function will vary... +;;;----------------------------------------------------------------------------- +;;; 3. Functions +;;;----------------------------------------------------------------------------- + +;;; Use LAMBDA to create anonymous functions. Functions always returns the +;;; value of the last expression. The exact printable representation of a +;;; function varies between implementations. (lambda () "Hello World") ; => #<FUNCTION (LAMBDA ()) {1004E7818B}> -;; Use funcall to call lambda functions -(funcall (lambda () "Hello World")) ; => "Hello World" +;;; Use FUNCALL to call anonymous functions + +(funcall (lambda () "Hello World")) ; => "Hello World" +(funcall #'+ 1 2 3) ; => 6 -;; Or Apply +;;; A call to FUNCALL is also implied when the lambda expression is the CAR of +;;; an unquoted list + +((lambda () "Hello World")) ; => "Hello World" +((lambda (val) val) "Hello World") ; => "Hello World" + +;;; FUNCALL is used when the arguments are known beforehand. Otherwise, use APPLY + +(apply #'+ '(1 2 3)) ; => 6 (apply (lambda () "Hello World") nil) ; => "Hello World" -;; De-anonymize the function -(defun hello-world () - "Hello World") +;;; To name a function, use DEFUN + +(defun hello-world () "Hello World") (hello-world) ; => "Hello World" -;; The () in the above is the list of arguments for the function -(defun hello (name) - (format nil "Hello, ~a" name)) +;;; The () in the definition above is the list of arguments +(defun hello (name) (format nil "Hello, ~A" name)) (hello "Steve") ; => "Hello, Steve" -;; Functions can have optional arguments; they default to nil +;;; Functions can have optional arguments; they default to NIL (defun hello (name &optional from) - (if from - (format t "Hello, ~a, from ~a" name from) - (format t "Hello, ~a" name))) - - (hello "Jim" "Alpacas") ;; => Hello, Jim, from Alpacas + (if from + (format t "Hello, ~A, from ~A" name from) + (format t "Hello, ~A" name))) -;; And the defaults can be set... -(defun hello (name &optional (from "The world")) - (format t "Hello, ~a, from ~a" name from)) +(hello "Jim" "Alpacas") ; => Hello, Jim, from Alpacas -(hello "Steve") -; => Hello, Steve, from The world +;;; The default values can also be specified -(hello "Steve" "the alpacas") -; => Hello, Steve, from the alpacas +(defun hello (name &optional (from "The world")) + (format nil "Hello, ~A, from ~A" name from)) +(hello "Steve") ; => Hello, Steve, from The world +(hello "Steve" "the alpacas") ; => Hello, Steve, from the alpacas -;; And of course, keywords are allowed as well... usually more -;; flexible than &optional. +;;; Functions also have keyword arguments to allow non-positional arguments (defun generalized-greeter (name &key (from "the world") (honorific "Mx")) - (format t "Hello, ~a ~a, from ~a" honorific name from)) + (format t "Hello, ~A ~A, from ~A" honorific name from)) -(generalized-greeter "Jim") ; => Hello, Mx Jim, from the world +(generalized-greeter "Jim") +; => Hello, Mx Jim, from the world (generalized-greeter "Jim" :from "the alpacas you met last summer" :honorific "Mr") ; => Hello, Mr Jim, from the alpacas you met last summer -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 4. Equality -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Common Lisp has a sophisticated equality system. A couple are covered here. +;;;----------------------------------------------------------------------------- +;;; 4. Equality +;;;----------------------------------------------------------------------------- + +;;; CL has a sophisticated equality system. Some are covered here. -;; for numbers use `=' -(= 3 3.0) ; => t -(= 2 1) ; => nil +;;; For numbers, use `=' +(= 3 3.0) ; => T +(= 2 1) ; => NIL -;; for object identity (approximately) use `eql` -(eql 3 3) ; => t -(eql 3 3.0) ; => nil -(eql (list 3) (list 3)) ; => nil +;;; For object identity (approximately) use EQL +(eql 3 3) ; => T +(eql 3 3.0) ; => NIL +(eql (list 3) (list 3)) ; => NIL -;; for lists, strings, and bit-vectors use `equal' -(equal (list 'a 'b) (list 'a 'b)) ; => t -(equal (list 'a 'b) (list 'b 'a)) ; => nil +;;; for lists, strings, and bit-vectors use EQUAL +(equal (list 'a 'b) (list 'a 'b)) ; => T +(equal (list 'a 'b) (list 'b 'a)) ; => NIL -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 5. Control Flow -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;----------------------------------------------------------------------------- +;;; 5. Control Flow +;;;----------------------------------------------------------------------------- ;;; Conditionals @@ -404,71 +462,75 @@ nil ; for false - and the empty list "this is false") ; else expression ; => "this is true" -;; In conditionals, all non-nil values are treated as true +;;; In conditionals, all non-NIL values are treated as true + (member 'Groucho '(Harpo Groucho Zeppo)) ; => '(GROUCHO ZEPPO) (if (member 'Groucho '(Harpo Groucho Zeppo)) 'yep 'nope) ; => 'YEP -;; `cond' chains a series of tests to select a result +;;; COND chains a series of tests to select a result (cond ((> 2 2) (error "wrong!")) ((< 2 2) (error "wrong again!")) (t 'ok)) ; => 'OK -;; Typecase switches on the type of the value +;;; TYPECASE switches on the type of the value (typecase 1 (string :string) (integer :int)) - ; => :int -;;; Iteration -;; Of course recursion is supported: +;;; Looping -(defun walker (n) - (if (zerop n) - :walked - (walker (- n 1)))) +;;; Recursion -(walker 5) ; => :walked +(defun fact (n) + (if (< n 2) + 1 + (* n (fact(- n 1))))) -;; Most of the time, we use DOLIST or LOOP +(fact 5) ; => 120 +;;; Iteration -(dolist (i '(1 2 3 4)) - (format t "~a" i)) +(defun fact (n) + (loop :for result = 1 :then (* result i) + :for i :from 2 :to n + :finally (return result))) -; => 1234 +(fact 5) ; => 120 -(loop for i from 0 below 10 - collect i) +(loop :for x :across "abc" :collect x) +; => (#\a #\b #\c #\d) -; => (0 1 2 3 4 5 6 7 8 9) +(dolist (i '(1 2 3 4)) + (format t "~A" i)) +; => 1234 -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 6. Mutation -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;----------------------------------------------------------------------------- +;;; 6. Mutation +;;;----------------------------------------------------------------------------- -;; Use `setf' to assign a new value to an existing variable. This was -;; demonstrated earlier in the hash table example. +;;; Use SETF to assign a new value to an existing variable. This was +;;; demonstrated earlier in the hash table example. (let ((variable 10)) (setf variable 2)) - ; => 2 +; => 2 +;;; Good Lisp style is to minimize the use of destructive functions and to avoid +;;; mutation when reasonable. -;; Good Lisp style is to minimize destructive functions and to avoid -;; mutation when reasonable. -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 7. Classes and Objects -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;----------------------------------------------------------------------------- +;;; 7. Classes and objects +;;;----------------------------------------------------------------------------- -;; No more Animal classes, let's have Human-Powered Mechanical -;; Conveyances. +;;; No more animal classes. Let's have Human-Powered Mechanical +;;; Conveyances. (defclass human-powered-conveyance () ((velocity @@ -479,14 +541,16 @@ nil ; for false - and the empty list :initarg :average-efficiency)) (:documentation "A human powered conveyance")) -;; defclass, followed by name, followed by the superclass list, -;; followed by slot list, followed by optional qualities such as -;; :documentation. +;;; The arguments to DEFCLASS, in order are: +;;; 1. class name +;;; 2. superclass list +;;; 3. slot list +;;; 4. optional specifiers -;; When no superclass list is set, the empty list defaults to the -;; standard-object class. This *can* be changed, but not until you -;; know what you're doing. Look up the Art of the Metaobject Protocol -;; for more information. +;;; When no superclass list is set, the empty list defaults to the +;;; standard-object class. This *can* be changed, but not until you +;;; know what you're doing. Look up the Art of the Metaobject Protocol +;;; for more information. (defclass bicycle (human-powered-conveyance) ((wheel-size @@ -500,7 +564,7 @@ nil ; for false - and the empty list (defclass recumbent (bicycle) ((chain-type :accessor chain-type - :initarg :chain-type))) + :initarg :chain-type))) (defclass unicycle (human-powered-conveyance) nil) @@ -509,8 +573,7 @@ nil ; for false - and the empty list :accessor number-of-rowers :initarg :number-of-rowers))) - -;; Calling DESCRIBE on the human-powered-conveyance class in the REPL gives: +;;; Calling DESCRIBE on the HUMAN-POWERED-CONVEYANCE class in the REPL gives: (describe 'human-powered-conveyance) @@ -532,47 +595,42 @@ nil ; for false - and the empty list ; Readers: AVERAGE-EFFICIENCY ; Writers: (SETF AVERAGE-EFFICIENCY) -;; Note the reflective behavior available to you! Common Lisp is -;; designed to be an interactive system +;;; Note the reflective behavior available. CL was designed to be an +;;; interactive system -;; To define a method, let's find out what our circumference of the -;; bike wheel turns out to be using the equation: C = d * pi +;;; To define a method, let's find out what our circumference of the +;;; bike wheel turns out to be using the equation: C = d * pi (defmethod circumference ((object bicycle)) (* pi (wheel-size object))) -;; pi is defined in Lisp already for us! +;;; PI is defined as a built-in in CL -;; Let's suppose we find out that the efficiency value of the number -;; of rowers in a canoe is roughly logarithmic. This should probably be set -;; in the constructor/initializer. +;;; Let's suppose we find out that the efficiency value of the number +;;; of rowers in a canoe is roughly logarithmic. This should probably be set +;;; in the constructor/initializer. -;; Here's how to initialize your instance after Common Lisp gets done -;; constructing it: +;;; To initialize your instance after CL gets done constructing it: (defmethod initialize-instance :after ((object canoe) &rest args) (setf (average-efficiency object) (log (1+ (number-of-rowers object))))) -;; Then to construct an instance and check the average efficiency... +;;; Then to construct an instance and check the average efficiency... (average-efficiency (make-instance 'canoe :number-of-rowers 15)) ; => 2.7725887 +;;;----------------------------------------------------------------------------- +;;; 8. Macros +;;;----------------------------------------------------------------------------- - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 8. Macros -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Macros let you extend the syntax of the language - -;; Common Lisp doesn't come with a WHILE loop- let's add one. -;; If we obey our assembler instincts, we wind up with: +;;; Macros let you extend the syntax of the language. CL doesn't come +;;; with a WHILE loop, however, it's trivial to write one. If we obey our +;;; assembler instincts, we wind up with: (defmacro while (condition &body body) "While `condition` is true, `body` is executed. - `condition` is tested prior to each execution of `body`" (let ((block-name (gensym)) (done (gensym))) `(tagbody @@ -584,47 +642,47 @@ nil ; for false - and the empty list (go ,block-name) ,done))) -;; Let's look at the high-level version of this: - +;;; Let's look at the high-level version of this: (defmacro while (condition &body body) "While `condition` is true, `body` is executed. - `condition` is tested prior to each execution of `body`" `(loop while ,condition do (progn ,@body))) -;; However, with a modern compiler, this is not required; the LOOP -;; form compiles equally well and is easier to read. +;;; However, with a modern compiler, this is not required; the LOOP form +;;; compiles equally well and is easier to read. -;; Note that ``` is used, as well as `,` and `@`. ``` is a quote-type operator -;; known as quasiquote; it allows the use of `,` . `,` allows "unquoting" -;; variables. @ interpolates lists. +;;; Note that ``` is used, as well as `,` and `@`. ``` is a quote-type operator +;;; known as quasiquote; it allows the use of `,` . `,` allows "unquoting" +;;; variables. @ interpolates lists. -;; Gensym creates a unique symbol guaranteed to not exist elsewhere in -;; the system. This is because macros are expanded at compile time and -;; variables declared in the macro can collide with variables used in -;; regular code. +;;; GENSYM creates a unique symbol guaranteed to not exist elsewhere in +;;; the system. This is because macros are expanded at compile time and +;;; variables declared in the macro can collide with variables used in +;;; regular code. -;; See Practical Common Lisp for more information on macros. +;;; See Practical Common Lisp and On Lisp for more information on macros. ``` -## Further Reading +## Further reading + +- [Practical Common Lisp](http://www.gigamonkeys.com/book/) +- [Common Lisp: A Gentle Introduction to Symbolic Computation](https://www.cs.cmu.edu/~dst/LispBook/book.pdf) -* [Keep moving on to the Practical Common Lisp book.](http://www.gigamonkeys.com/book/) -* [A Gentle Introduction to...](https://www.cs.cmu.edu/~dst/LispBook/book.pdf) +## Extra information -## Extra Info +- [CLiki](http://www.cliki.net/) +- [common-lisp.net](https://common-lisp.net/) +- [Awesome Common Lisp](https://github.com/CodyReichert/awesome-cl) +- [Lisp Lang](http://lisp-lang.org/) -* [CLiki](http://www.cliki.net/) -* [common-lisp.net](https://common-lisp.net/) -* [Awesome Common Lisp](https://github.com/CodyReichert/awesome-cl) -## Credits. +## Credits Lots of thanks to the Scheme people for rolling up a great starting point which could be easily moved to Common Lisp. |