[common-lisp/en]: clean up and add more information

This commit is contained in:
Rommel Martinez 2018-03-01 21:25:04 +08:00
parent 9f33f260dc
commit 6968c57248

View File

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