Lisp Crash Course
Todo: proper formatting and explanation text.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATATYPES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CL-USER> 123 123 CL-USER> "bla" "bla" CL-USER> * "bla" CL-USER> #\c #\c CL-USER> #b111 7 CL-USER> 1/5 1/5 CL-USER> 3/6 1/2 CL-USER> (* * **) 1/10 CL-USER> 1e-5 1.e-5 CL-USER> (+ 1 1e-5) 1.00001 CL-USER> 1.0 1.0 CL-USER> 1e-5 1.e-5 CL-USER> (+ 1 *) 1.00001 CL-USER> (1+ **) 1.00001 CL-USER> 1d0 1.0d0 CL-USER> 2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222 2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222 CL-USER> (* * 333333333333333333333333333333333333333333333) 740740740740740740740740740740740740740740739999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999925925925925925925925925925925925925925925926 CL-USER> 'cat CAT CL-USER> (describe 'cat) COMMON-LISP-USER::CAT [symbol] ; No value CL-USER> (+ 1 2 3) 6 CL-USER> (max 3 6) 6 CL-USER> (describe 'max) COMMON-LISP:MAX [symbol] MAX names a compiled function: Lambda-list: (NUMBER &REST MORE-NUMBERS) Declared type: (FUNCTION (REAL &REST REAL) (VALUES REAL &OPTIONAL)) Derived type: (FUNCTION (T &REST T) (VALUES NUMBER &OPTIONAL)) Documentation: Return the greatest of its arguments; among EQUALP greatest, return the first. Known attributes: foldable, flushable, unsafely-flushable, movable, explicit-check Source file: SYS:SRC;CODE;NUMBERS.LISP ; No value CL-USER> (list 'max 3 6) (MAX 3 6) ;;;;;;;;;;;;;;;; (inspect) CL-USER> * (MAX 3 6) CL-USER> (first *) MAX CL-USER> (second **) 3 CL-USER> (eval ***) 6 CL-USER> '(max 3 6) (MAX 3 6) CL-USER> (second *) 3 CL-USER> '() NIL CL-USER> () NIL CL-USER> (describe ()) COMMON-LISP:NIL [null] NIL names a constant variable: Value: NIL NIL names a primitive type-specifier: (undocumented) ; No value CL-USER> (describe 't) COMMON-LISP:T [symbol] T names a constant variable: Value: T T names the built-in-class #<BUILT-IN-CLASS T>: Class precedence-list: T Direct subclasses: SEQUENCE, ARRAY, SIMD-PACK, NUMBER, SB-KERNEL::RANDOM-CLASS, SB-KERNEL:FDEFN, SB-KERNEL:LRA, SB-KERNEL:CODE-COMPONENT, WEAK-POINTER, SYSTEM-AREA-POINTER, SYMBOL, CHARACTER, SB-PCL::SLOT-OBJECT, STREAM, FUNCTION No direct slots. T names a primitive type-specifier: (undocumented) ; No value CL-USER> (describe 'nil) COMMON-LISP:NIL [null] NIL names a constant variable: Value: NIL NIL names a primitive type-specifier: (undocumented) ; No value CL-USER> (describe t) COMMON-LISP:T [symbol] T names a constant variable: Value: T T names the built-in-class #<BUILT-IN-CLASS T>: Class precedence-list: T Direct subclasses: SEQUENCE, ARRAY, SIMD-PACK, NUMBER, SB-KERNEL::RANDOM-CLASS, SB-KERNEL:FDEFN, SB-KERNEL:LRA, SB-KERNEL:CODE-COMPONENT, WEAK-POINTER, SYSTEM-AREA-POINTER, SYMBOL, CHARACTER, SB-PCL::SLOT-OBJECT, STREAM, FUNCTION No direct slots. T names a primitive type-specifier: (undocumented) ; No value CL-USER> '(if t (print "true") (print "false")) (IF T (PRINT "true") (PRINT "false")) CL-USER> (eval *) "true" "true" ;;;;;;;;;;;;; jede eingabe: erstmal datenstruktur, dann kompiliert, dann ausgefuehrt CL-USER> (IF T ;;;;;;;;;;;;; left click (PRINT "true") (PRINT "false")) "true" "true" CL-USER> (MaX 2 3 4) 4 CL-USER> (when 0 ;;;;;;;;;;;;; compiler? (print "hoho")) "hoho" "hoho" CL-USER> #(5 4 3 2) #(5 4 3 2) CL-USER> (describe *) #(5 4 3 2) [simple-vector] Element-type: T Length: 4 ; No value ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; FUNCTIONS ;;;;;;;;;;;;;;;;;;; CL-USER> (defun say-hello () (print "helloooo")) SAY-HELLO CL-USER> (say-hello) "helloooo" "helloooo" CL-USER> (describe 'say-hello) COMMON-LISP-USER::SAY-HELLO [symbol] SAY-HELLO names a compiled function: Lambda-list: () Derived type: (FUNCTION NIL (VALUES (SIMPLE-ARRAY CHARACTER (8)) &OPTIONAL)) Source form: (SB-INT:NAMED-LAMBDA SAY-HELLO NIL (BLOCK SAY-HELLO (PRINT "helloooo"))) ; No value ;;;;;;;;;;;;;;;;;;;;;;; inspect compiled code CL-USER> (sb-ext:gc :full t) NIL CL-USER> (list 'defun 'return-a '(a) 'a) (DEFUN RETURN-A (A) A) CL-USER> (eval *) RETURN-A CL-USER> (return-a 5) 5 CL-USER> (defun if ()) ; in: DEFUN IF ; (SB-INT:NAMED-LAMBDA IF ; NIL ; (BLOCK IF)) ; ; caught ERROR: ; Special form is an illegal function name: IF ; ; compilation unit aborted ; caught 1 fatal ERROR condition ; caught 1 ERROR condition ; Evaluation aborted on #<SB-INT:SIMPLE-PROGRAM-ERROR "Special form is an illegal function name: ~S" {100C93BE63}>. CL-USER> (defun when ()) ; ; compilation unit aborted ; caught 1 fatal ERROR condition ; Evaluation aborted on #<SYMBOL-PACKAGE-LOCKED-ERROR "proclaiming ~S as a function" {100CB00B03}>. ;;;;;;;;;;;;;;;;;;;;;; sleep loop with compiled function and .fasl CL-USER> (defvar *db* '((Anna 1987) (Bob 1899) (Charlie 1980))) *DB* CL-USER> (defun name-and-year (id) (values (first (nth (- id 1) *db*)) (second (nth (- id 1) *db*)))) NAME-AND-YEAR CL-USER> (name-and-year 2) BOB 1899 CL-USER> (multiple-value-bind (name year) (name-and-year 3) (format t "~a was born in ~a.~%" name year)) CHARLIE was born in 1980. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SYMBOLS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CL-USER> (setf max 234) ;;;;;;;;;;;;;;;;; max = 234 ; in: SETF MAX ; (SETF MAX 234) ; ==> ; (SETQ MAX 234) ; ; caught WARNING: ; undefined variable: MAX ; ; compilation unit finished ; Undefined variable: ; MAX ; caught 1 WARNING condition 234 CL-USER> max ;;;;;;; <- variable, da ohne klammer und apostroph 234 CL-USER> ;;;;;;;;;;;defvar... CL-USER> 'max MAX ;;;;;;;;; inspect ((((((((((((((( property lists, only if asked CL-USER> (setf (get 'max 'my-property) 3) 3 CL-USER> (setf (get 'max 'my-other-property) 5) 5 ;;;; inspect CL-USER> (setf my-var '(my-prop 3 my-other-prop 5)) CL-USER> (getf my-var 'my-prop) 3 CL-USER> 'my-var MY-VAR ;;;;;;;;; inspect ))))))))))))))) CL-USER> '%awesome?or-not->symbol_1234фыва^2 %AWESOME?OR-NOT->SYMBOL_1234ФЫВА^2 CL-USER> (setf %awesome?or-not->symbol_1234фыва^2 234) ; in: SETF %AWESOME?OR-NOT->SYMBOL_1234ФЫВА^2 ; (SETF %AWESOME?OR-NOT->SYMBOL_1234ФЫВА^2 234) ; ==> ; (SETQ %AWESOME?OR-NOT->SYMBOL_1234ФЫВА^2 234) ; ; caught WARNING: ; undefined variable: %AWESOME?OR-NOT->SYMBOL_1234ФЫВА^2 ; ; compilation unit finished ; Undefined variable: ; %AWESOME?OR-NOT->SYMBOL_1234ФЫВА^2 ; caught 1 WARNING condition 234 CL-USER> %awesome?or-not->symbol_1234фыва^2 234 ;;;;;;;;;;; inspect symbol CL-USER> (intern "YOYO") YOYO NIL CL-USER> (dotimes (i 3) (set (intern (format nil "CAT-~a" i)) i)) NIL CL-USER> cat-2 2 CL-USER> (intern "hello world") |hello world| NIL ;;;;;;;;;;;;;;;;;;;;;;;;; FUNCTIONS AS FIRST CLASS CITIZENS ;;;;;;;;;;;;;;;;;;;;;;; CL-USER> (describe '>) COMMON-LISP:> [symbol] > names a compiled function: Lambda-list: (NUMBER &REST MORE-NUMBERS) Declared type: (FUNCTION (REAL &REST REAL) (VALUES (MEMBER T NIL) &OPTIONAL)) Derived type: (FUNCTION (T &REST T) (VALUES (MEMBER NIL T) &OPTIONAL)) Documentation: Return T if its arguments are in strictly decreasing order, NIL otherwise. Known attributes: foldable, flushable, unsafely-flushable, movable, predicate, explicit-check Source file: SYS:SRC;CODE;NUMBERS.LISP ; No value CL-USER> (describe #'>) #<FUNCTION >> [compiled function] Lambda-list: (NUMBER &REST MORE-NUMBERS) Declared type: (FUNCTION (REAL &REST REAL) (VALUES (MEMBER T NIL) &OPTIONAL)) Derived type: (FUNCTION (T &REST T) (VALUES (MEMBER NIL T) &OPTIONAL)) Documentation: Return T if its arguments are in strictly decreasing order, NIL otherwise. Known attributes: foldable, flushable, unsafely-flushable, movable, predicate, explicit-check Source file: SYS:SRC;CODE;NUMBERS.LISP ; No value CL-USER> (sort '(28672 3766 3 7 111111 8 5 -235) #'>) (111111 28672 3766 8 7 5 3 -235) CL-USER> (sort '(28672 3766 3 7 111111 8 5 -235) #'<) (-235 3 5 7 8 3766 28672 111111) CL-USER> (defun fancy-sort (x y) (> (abs x) (abs y))) FANCY-SORT CL-USER> (sort '(28672 3766 3 7 111111 8 5 -235) #'fancy-sort) (111111 28672 3766 -235 8 7 5 3) CL-USER> (sort '(28672 3766 3 7 111111 8 5 -235) (lambda (x y) (> (abs x) (abs y)))) (111111 28672 3766 -235 8 7 5 3) CL-USER> (setf my-list '(10 -25 47 20 -46 0)) ; in: SETF MY-LIST ; (SETF MY-LIST '(10 -25 47 20 -46 0)) ; ==> ; (SETQ MY-LIST '(10 -25 47 20 -46 0)) ; ; caught WARNING: ; undefined variable: MY-LIST ; ; compilation unit finished ; Undefined variable: ; MY-LIST ; caught 1 WARNING condition (10 -25 47 20 -46 0) CL-USER> (mapcar #'abs my-list) (10 25 47 20 46 0) CL-USER> (mapcar (lambda (x) (* x -1)) my-list) (-10 25 -47 -20 46 0) CL-USER> (+ '(1 2 3) '(2 3 4)) ; Evaluation aborted on #<TYPE-ERROR expected-type: NUMBER datum: (1 2 3)>. CL-USER> (mapcar #'+ '(1 2 3) '(2 3 4)) (3 5 7) CL-USER> (expt 2 3) 8 CL-USER> (reduce #'expt '(2 3 2)) 64 CL-USER> (defun x^10 (x) (expt x 10)) X^10 CL-USER> (dolist (elem '(2 3 4)) (format t "~a^10 = ~a~%" elem (x^10 elem))) 2^10 = 1024 3^10 = 59049 4^10 = 1048576 NIL CL-USER> (asdf:load-system :alexandria) T CL-USER> (dolist (elem '(2 3 4)) (format t "~a^10 = ~a~%" elem (funcall (alexandria:curry #'expt 10) elem))) 2^10 = 100 3^10 = 1000 4^10 = 10000 NIL CL-USER> (dolist (elem '(2 3 4)) (format t "~a^10 = ~a~%" elem (funcall (alexandria:rcurry #'expt 10) elem))) 2^10 = 1024 3^10 = 59049 4^10 = 1048576 NIL CL-USER> my-list (10 -25 47 20 -46 0) CL-USER> (first *) 10 CL-USER> (rest **) (-25 47 20 -46 0) CL-USER> (labels ((invert (the-list &optional acc) (if (null the-list) acc (invert (rest the-list) (cons (first the-list) acc))))) (invert my-list)) ; (INVERT MY-LIST) ; ; caught WARNING: ; undefined variable: MY-LIST ; ; compilation unit finished ; Undefined variable: ; MY-LIST ; caught 1 WARNING condition (0 -46 20 47 -25 10) CL-USER> (let ((counter 0)) (defun count-cats () (incf counter))) COUNT-CATS CL-USER> (count-cats) 1 CL-USER> (count-cats) 2 CL-USER> (count-cats) 3 ;;;;;;;;;; inspect CL-USER> counter ; Evaluation aborted on #<UNBOUND-VARIABLE COUNTER {10097B09E3}>. CL-USER> (defclass shape () ((color :accessor get-shape-color :initarg :set-color) (center :accessor shape-center :initarg :center :initform '(0 . 0)))) #<STANDARD-CLASS SHAPE> CL-USER> (defvar *red-shape* (make-instance 'shape :set-color 'red)) *RED-SHAPE* CL-USER> *red-shape* #<SHAPE {100A0E5703}> CL-USER> (describe *) #<SHAPE {100A0E5703}> [standard-object] Slots with :INSTANCE allocation: COLOR = RED CENTER = (0 . 0) ; No value CL-USER> (defclass circle (shape) ((radius :initarg :radius))) #<STANDARD-CLASS CIRCLE> CL-USER> (defvar *circle* (make-instance 'circle :set-color 'green :radius 10)) *CIRCLE* CL-USER> *circle* #<CIRCLE {100A194D93}> ;;;;;;;;;;; inspect CL-USER> (defgeneric area (x) (:documentation "Calculates area of object of type SHAPE.")) #<STANDARD-GENERIC-FUNCTION AREA (0)> CL-USER> (area 1) ; Evaluation aborted on #<SIMPLE-ERROR "~@<There is no applicable method for the generic function ~2I~_~S~ ; ~I~_when called with arguments ~2I~_~S.~:>" {100A36A1D3}>. CL-USER> (defmethod area (x) (error "AREA is only applicable to SHAPE instances")) #<STANDARD-METHOD AREA (T) {100A550473}> CL-USER> (area 1) ; Evaluation aborted on #<SIMPLE-ERROR "AREA is only applicable to SHAPE instances" {100A675C93}>. CL-USER> (defmethod area ((obj shape)) (error "We need more information about OBJ to know its area")) #<STANDARD-METHOD AREA (SHAPE) {100A84F883}> CL-USER> (area *red-shape*) ; Evaluation aborted on #<SIMPLE-ERROR "We need more information about OBJ to know its area" {100A855053}>. CL-USER> (defmethod area ((obj string)) (print obj)) #<STANDARD-METHOD AREA (STRING) {100AA4A533}> CL-USER> (area "bla") "bla" "bla" CL-USER> (defmethod area ((obj circle)) (* pi (expt (slot-value obj 'radius) 2))) #<STANDARD-METHOD AREA (CIRCLE) {100AB2E5D3}> CL-USER> (area *circle*) 314.1592653589793d0 CL-USER> (defmethod area :before (obj) (print "before area")) #<STANDARD-METHOD AREA :BEFORE (T) {100AB92773}> CL-USER> (area *circle*) "before area" 314.1592653589793d0 CL-USER> (defmethod area :around ((obj shape)) (format t "hallo~%")) #<STANDARD-METHOD AREA :AROUND (SHAPE) {100AD15683}> CL-USER> (area *red-shape*) hallo NIL CL-USER> (defmethod area :around ((obj shape)) (format t "hallo~%") (call-next-method)) STYLE-WARNING: redefining AREA :AROUND (#<STANDARD-CLASS SHAPE>) in DEFMETHOD #<STANDARD-METHOD AREA :AROUND (SHAPE) {100AE65183}> CL-USER> (area *red-shape*) hallo "before area" ; Evaluation aborted on #<SIMPLE-ERROR "We need more information about OBJ to know its area" {100AE6C783}>. CL-USER> (defmethod :area :around ((obj circle)) (* (call-next-method) 2)) STYLE-WARNING: Implicitly creating new generic function :AREA. #<STANDARD-METHOD :AREA :AROUND (CIRCLE) {100B122543}> CL-USER> (area *circle*) hallo "before area" 314.1592653589793d0 CL-USER> (defgeneric awesome-function (x) (:method-combination +)) #<STANDARD-GENERIC-FUNCTION AWESOME-FUNCTION (0)> CL-USER> (defmethod awesome-function + ((x number)) x) #<STANDARD-METHOD AWESOME-FUNCTION + (NUMBER) {100B20C413}> CL-USER> (awesome-function 2) 2 CL-USER> (typep 3 'number) T CL-USER> (typep 3 'integer) T CL-USER> (defmethod awesome-function + ((x integer)) x) #<STANDARD-METHOD AWESOME-FUNCTION + (INTEGER) {100B400293}> CL-USER> (awesome-function 2) 4 CL-USER> (defgeneric list-function (x) (:method-combination list)) #<STANDARD-GENERIC-FUNCTION LIST-FUNCTION (0)> CL-USER> (defmethod list-function list ((x number)) x) #<STANDARD-METHOD LIST-FUNCTION LIST (NUMBER) {100B66B623}> CL-USER> (defmethod list-function list ((x integer)) x) #<STANDARD-METHOD LIST-FUNCTION LIST (INTEGER) {100B6CBC43}> CL-USER> (list-function 123) (123 123) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MACROS ;;;;;;;;;;;;;;;;;;;;;;;; CL-USER> (defmacro square (&whole form arg) (if (atom arg) `(expt ,arg 2) (case (car arg) (square (if (= (length arg) 2) `(expt ,(nth 1 arg) 4) form)) (expt (if (= (length arg) 3) (if (numberp (nth 2 arg)) `(expt ,(nth 1 arg) ,(* 2 (nth 2 arg))) `(expt ,(nth 1 arg) (* 2 ,(nth 2 arg)))) form)) (otherwise `(expt ,arg 2))))) SQUARE CL-USER> (square 3) 9 CL-USER> (square -3) 9 CL-USER> (square (square 2)) 16 CL-USER> (square (expt 2 5)) 1024
Hello world function
As any other language we start with hello-world function :
CL-USER> (defun hello-world () (format t “hello world”)
We will talk about defun more later. But now we consider some basic mathematical operator in lisp. You can run them in your repl and track the results :
CL-USER>(+ 1 1e-5) CL-USER>(+ 1 2 3) CL-USER>(* * 2)
Defun
By using defun we can define new function with different arguments. Any symbol can be used as a function name and it is not mandatory like most of the other languages to only use alphabetic characters for the function name.
CL-USER>(defun function-name (parameters*) (body-forms*))
When we want to call the function, we should pass the arguments that we define in the definition of the function. If the function takes no arguments, the list is empty, written as (). Anyway you should consider that we can handle different type of parameters, constant number of parameters, optional parameters and multiple parameters.
CL-USER>(defun function-name (arg1 arg2 arg3 arg4) (list arg1 arg2 arg3 arg4))
Optional arguments
Sometime a function might have some parameters which only consider by some of the callers of the function. perhaps because there's a reasonable default value. If in any cases some arguments are optional, in this situation after the names of any required parameters, place the symbol &optional followed by the names of the optional parameters.
CL-USER>(defun function-name-optional (arg1 arg2 &optional arg3 arg4))
Please find the example below as a function that can take some optional arguments.
CL-USER>(defun optional-function (a b &optional c d) (list a b c d))
After the function is called arguments are assigned to the required parameters. Since then if any other parameters are left, their values are given to the optional parameters. If the arguments run out before the optional parameters do, the remaining optional parameters are bound to the value NIL.
CL-USER>(optional-function 1 2 3) (1 2 3 NIL)
Of course most of the time it is not acceptable to have parameter that is NIL so it is better to assign a default value to the optional parameter.
CL-USER>(defun optional-function (a &optional (b 10)) (list a b)) CL-USER>(optional-function 1 2) (1 2) CL-USER>(optional-function 1) (1 10)
Example : define a function that can take 3 numbers and get the average of them
CL-USER>(defun get-avg (num-1 num-2 num-3) (/ (+ num-1 num-2 num-3) 3)) CL-USER>(format t “avg 10 & 20 & 30 = -a ~%” (get-avg 10 20 30))
Multiple values
if in any cases multiple values are needed :
CL-USER>(defun foo (&key a b c) (list a b c)) (NIL NIL NIL) CL-USER>(foo :a 1) (1 NIL NIL) CL-USER>(foo :a 1 :c 3) (1 NIL 3)
Imagine a situation that the option has three parameters but you only want to assign value to one of them. Now if that one parameter is the first one, it is ok. but if you want to assign a value to the second or third parameter, what will you do? This the problem of optional parameters. Here we have another symbol which is call &key.
CL-USER>(defun function-name (&rest args))
Please notice that any number of lisp expression can be part of defun. after you call the function they will be evaluated in order and the last expression is returned as the value of the function. If you want to immediately return anything anywhere in a function it is possible by using return-from.
defvar
There are two ways to create global variables in common lisp, defvar and defparameter. Their syntaxs are almost simple by taking variable name, an initial value and an optional documentation string. after that anywhere to refer to the current binding of the global variable. Notice that global variable's name start and end with *. It is important to mention that defparameter always assign the initial value while defvar does so only if the variable is undefined. We can say if you want to define a variable that you want to keep it even if later you made a change to the source you should use defvar.
By using defvar we can define global variable :
CL-USER>(defvar *total* 0) CL-USER>(defun sum (&rest numbers) CL-USER>(dolist (i numbers) (setf *total* (+ *total* I)) ))
We will talk about setf and dolist later but for now you should know,
setf can be used to setting the value to a variable
dolist create a loop through a list
List
A list is a sequence. The very first element of the list is cons cell. If you want to build a list you should assemble some cons cells together.But before that we will show how you can define a list directly. To define a list we can use :
CL-USER>(list ‘(max 2 3))
Also
CL-USER>(defvar *list* (list max 2 3))
To take the first element :
CL-USER>(first *)
To take the second element :
CL-USER>(second **)
Instead of * you can use the name of the list (here it is list).
Now let's back to cons. In the following example we will define a list after that you will se how we can do it with cons.
CL-USER>(list 1 2 3) (1 2 3) CL-USER>(cons 1 (cons 2(cons 3 nil))) (1 2 3)
car/cdr or first/rest
We talked about the cons, however we didn't say that what they really is? Cons has a sequential structure that is containing two components that called car and cdr in data structure. Each cons cell is a object that has a pairs of values. The two values in a cons cell are called the car and the cdr. The car function is used to access the first value and the cdr function is used to access the second value.
CL-USER>(car '(1 2 3)) 1 CL-USER>(cdr '(1 2 3)) (2 3)
Now let's have some fun! In common lisp we can use caar, cddr, cadr, and … . Now let's check some of them :
CL-USER>(caar (list 1 2 3)) Error CL-USER>(caar (list (list 1 2) 3)) 1 CL-USER>(cadr (list (list 1 2) (list 3 4))) (3 4) CL-USER>(caadr (list (list 1 2) (list 3 4))) 3
append
it is possible to add a list or some elements to the list by using append.
CL-USER>(append (list 1 2) (list 3 4)) (1 2 3 4) CL-USER>(append '(1) (list 3 4)) (1 3 4) CL-USER>(append '(max) (list 3 4)) (MAX 3 4)
subst
With subst we can substitute any element in a list by other element.
CL-USER>(subst 'max 2 '(1 2 3 4) (1 MAX 3 4) CL-USER>(subst 'one 1 '(1 2 3 4) (ONE 2 3 4)
Now by combining the list and defun we will implement something that you can ask name and birth of people by using their place number in the list:
CL-USER>(defvar *db* ‘((anna 1987) (bob 1899) (charlie 1980))) CL-USER>(defun name-and-birth-year (id) (values (first (nth (-id 1) *db*)) (second (nth (-id 1) *db*))))
To call it :
CL-USER>(name-and-birth-year 2)
Or it is more beautiful to use it like :
CL-USER>(multiple-value-bind (name year) (name-and-birth-year 2) (format t “~a was born in ~a .~%” name year))
Another example of using defun :
CL-USER>(defun football-player (name post team) (list :name name :post post :team team))
To add an example player to it :
CL-USER>(football-player “CR7” “Winger” “Manchester united)
If you want to check if something is a number you should use numberp If you want to check if a number is an even number you should use evenp If you want to check if a number is an odd number you should use oddp Remove-if-not is a function that can check a list. In the following example you will remove every number from the list if they Aren’t even :
CL-USER>(remove-if-not #’evenp ‘(1 2 3 4 5 6 7 8 9))
setf & setq
Both of them are used to assign a value to a other data-type, but setq is only using for assign a value to a symbol, however setf can be used for setting value of any data-type and not only symbols. So Setf can be used in place of setq but setq cannot be used in place of setf. Since one data-type will take the value, so setf and setq must have even number of arguments. But the can take any number of pairs.
CL-USER>(setq a 1 b 2 c 3) CL-USER>(setf a 1 b 2 c 3) CL-USER>(setf (car '(a b cd)) 2) 2
For the above example the (car ‘(a b cd)) will give A and then the setf give value 2 to it.
Lambda function
If you want to define a function without giving it a name it could be possible by using lambda function. Lambda function is also known as anonymous functions.
CL-USER>(lambda (parameter) (body))
* The parameters are the variable in the expression
* The body is the mathematical logic expression
Consider the following example as lambda instance
CL-USER>(remove-if-not #’(lambda (x) (= 0 (mod x 2))) ‘(1 2 3 4 5 6 7 8 9 10)) CL-USER>(mapcar (lambda (n) (/ n 2)) ‘(2 4 6))
if marco
The if macro is followed by a test clause that evaluates to t or nil.
CL-USER>(if test-form then-form [else-form])
Everytime the test-form evaluated, the then-form will be evaluated if the value of the test-form was non-NIL. Otherwise, the else-form value return. If condition is NIL and there's no else-form, then the IF returns NIL.
For example :
CL-USER>(setf a 10) CL-USER>(if (< a 20) (format t "~% a is less than 20") (format t "~% a is more than 20"))
For better understanding run these three codes in your lisp :
CL-USER>(if ‘() ‘true ‘false) CL-USER>(if ‘(1) ‘true ‘false) CL-USER>(if (oddp 5) ‘odd-number ‘even-number)
Unlike of other programming languages you can't excute multiple operation with then-form and else-form and both of them are restricted to a single one. But the good news is you can do a sequence of action with some other syntax.
Progn
If you want to do more than one thing in if form, you should use progn. Progn excutes different forms in order and returns the value of the last form.
CL-USER>(defun *number-was-odd* nil) CL-USER>(if (oddp 5) (progn (setf (*number-was-odd* t) ‘odd-number) ‘even-number)
When & Unless
Instead of using progn, you can use when and unless. With when , all the enclosed expressions are evaluated when the condition is true. With unless , all the enclosed expressions are evaluated when the condition is false.
CL-USER>(defvar *number-is-odd* nil) CL-USER>(when (oddp 5) (setf *number-is-odd* t) 'odd-number) CL-USER>(unless (oddp 4) (setf *number-is-odd* nil) 'even-number)
Cond
The problem is these commands can’t do anything when the condition evaluates in the opposite way; they just return nil and do nothing. If you want to anything you can use cond macro.
CL-USER>(setf *num* 7) CL-USER>(cond ((> num 0) (format t” number is positive”) (format t “number is ~a %” num)) ((< num 0) (format t” number is negative”) (format t “number is ~a %” num)) ((= num 0) (format t “number is ~a %” num))))
defparameter
CL-USER>(defparameter *fruit* 'apple) CL-USER>(cond ((eq *fruit* 'apple) 'its-an-apple) ((eq *fruit* 'orange) 'its-an-orange))
Case
Another lisp command form that can handle different conditions is case that is easier to use.
CL-USER>setq day 4) CL-USER>(case day (1 (format t "~% Monday")) (2 (format t "~% Tuesday")) (3 (format t "~% Wednesday")) (4 (format t "~% Thursday")) (5 (format t "~% Friday")) (6 (format t "~% Saturday")) (7 (format t "~% Sunday"))) CL-USER>(setq val1 2) CL-USER>(<code lisp>case val1 (1 (format t "you selected number 1")) (2 (format t "you selected number 2")) (3 (format t "you selected number 3")) (4 (format t "you selected number 4")) (5 (format t "you selected number 5")) )
And & Or
And and Or operators give true or false.
CL-USER>(and (oddp 5) (oddp 7) (oddp 9)) CL-USER>(or (oddp 4) (oddp 7) (oddp 8))
Loop in lisp
There are some looping constructions in the lisp. For example, loop, dolist, dotimes and do.
In the following you will find some examples of using loop. Its Structure is easy and doesn’t need any extra explanation.
Loop
The form-body will evaluate in the loop unless it reaches a limitation or you use return to break it out.
CL-USER>(loop body-form*) CL-USER>(loop for i below 5 do (print I)) CL-USER>(loop for I from 1 to 10 summing (expt x 2)) CL-USER>(setq a 10) CL-USER>(loop (setq a (+ a 1)) (write a) (when (> a 17) (return a)) )
Dolist
dolist will loop across the item of the list. It is important to mention that the body-form will be evaluated for each item in the list.
CL-USER>(dolist (var list-form) body-form*)
The list-form will be the list of elements that are iterated
The body-form are present in the loop
CL-USER>(dolist (x ‘(1 2 3)) (print x)) CL-USER>(dolist (n '(1 2 3 4 5 6 7 8 9)) (format t "~% Number: ~d Square: ~d" n (* n n)) )
Dotimes
With dotimes you can do looping for some fixed number of iterations. So the dotimes is quite similar to dolist, but you should consider that it loops for a particular number of times.
CL-USER>(dotimes (var count-form) (body-form*)
CL-USER>(dotimes (I 4) (print I)) CL-USER>(dotimes (n 11) (print n) (prin1 (* n n)) )
Do
Do is more general than two others.
CL-USER>(do (var-form*) (end-form*) body-form*)
Var-form consists of some varibles that we defined and their initial values And how each iteration will affect it
end-form composed of how the loop finish and value returned. It will evaluated at the start of the iteration.
CL-USER>(do ((x 0 (+ 2 x)) (y 20 ( - y 2))) ((= x y)(- x y)) (format t "~% x = ~d y = ~d" x y) )
Using collect to return a list :
CL-USER>(loop for x in ‘(1 2 3) collect (* x 10))
Defmarco
One of the greatest thing about lisp is that you can extend the syntax by using defmacro.
The difference between defmacro and defun is defun returning values, but the defmacro will return a lisp form.
CL-USER>(defmacro macro-name (parameter*) "Optional documentation string." body-form*)
HASH TABLE
A collection of key-values pairs. It uses the key to access the elements in the collection. Anytime you need to access to some elements only by using a key you can use hash table.
By using make-hash-table you can create a hash table.
CL-USER>(defparameter Team (make-hash-table))
Set the number of 7 to CR7 and 18 to Paul schools:
CL-USER>(setf (gethash '7 Team) '(CR7)) CL-USER>(setf (gethash '18 Team) '(Paul schools))
By using gethash it is possible to get the value that is stored in the key 7:
CL-USER>(gethash '7 Team)
maphash will run a function on the entire of hash table:
CL-USER>(maphash #'(lambda (k v) (format t "~a = ~a~%" k v)) Team)
If you want to remove on entry, it is possible by using remhash:
CL-USER>(remhash '18 Team)
Defclass
By using defclass you can define your own classes. Defclass is an instance of a data type and needs three arguments which is name, relation to other classes (like super class) and the name of the slots that make up instances of the class. The very simple form of defclass is like this :
CL-USER>(defclass name (superclass-name*) (slot-specifier*))
It is important to mention that it is possible to use any symbol in the class name. Also notice that you can have one name for a class, function and variable since they are in the different namespaces. In the following lines you will find some examples:
CL-USER>(defclass Team () (color country))
If you want to make an example of team it is possible by using make-instance:
CL-USER>(defparameter *Man utd* (make-instance 'Team))
Set the values for Man utd :
CL-USER>(setf (slot-value *Team* 'color) "Red") CL-USER>(setf (slot-value *Team* 'country) "England")
You can also get the value if you want :
CL-USER>(format t "Man utd is ~a and is playing in ~a ~%" (slot-value *Team* 'color) (slot-value *Team* 'country))
In defclass we have some options:
- initarg : New instance can be constructed
- initform : Default value for the instance
- accessor : It is both a getter and a setter
CL-USER>(defclass Team () ((name :initarg :name :initform (error "Must provide a name")) (color :initarg :sound :initform "No color set" :accessor Team-color) ) )
For example :
CL-USER>(defparameter *Man utd* (make-instance 'Team :name "Man utd" :color "red"))
As always we get the output like this :
CL-USER>(format t "~a is ~a ~%" (slot-value *man utd* 'name) (slot-value *man utd* 'color))
Another example for defclass is:
CL-USER>(defclass shape () ((color :accessor get-shape-color :initarg :set-color) (center :accessor shape-center :initarg :center :initform '(0 . 0))))
CL-USER>(defvar *red-shape* (make-instance 'shape :set-color 'red))
We will define another class and give the shape class as superclass to it :
CL-USER>(defclass circle (shape) ((radius :initarg :radius)))
circle will be one instance of circle class. We define the color and radius for our instance. later by using radius we can calculate the are of the circle. However before that we should define a method that with which you can compute the are.
CL-USER> (defvar *circle* (make-instance 'circle :set-color 'green :radius 10))
First you can run this method on your repl. However for sure for calculating the area we need more information about the shape!
CL-USER> (defmethod area ((obj shape)) (error "We need more information about OBJ to know its area"))
Here we define the method that can compute the area of a circle.
CL-USER> (defmethod area ((obj circle)) (* pi (expt (slot-value obj 'radius) 2)))
Now by calling the method we can compute the circle instance that we defined before. You can define more methods for different shape for example, triangle.
CL-USER> (area *circle*)
The NIL
First please look at this example. Can you recognize why the output is true?
CL-USER> (if '(1) (print "true") (print "false")) "true" "true"
To know why '(1) was considered as true value you should know the output will be true for any value that it is not equivalent to an empty list. But what about false… . In common lisp false symbol is NIL. There are four type of NIL in the common lisp. All of the following expressions are considered as empty list and so NIL:
'(), (), 'nil and nil. You can check the equivalency of them like this two by two:
CL-USER> (eq '() nil) T
Let & Let*
In most function definition in lisp you should use let expression and it is a special form in lisp that is used to attach or assign a symbol to a value.
CL-USER> (let (variable*) body-form*)
Each variable has an initialization that is assigned by let. In some cases the initialization value will be NIL. For example the following let form will assign 1 5 and NIL to three variables x, y and z.
CL-USER> (let ((x 1) (y 5) z) ...)
The value that will return in the body of the let is the value of the last expression.
Another way to assign a value to a variable is let*. there is a difference between let and let* which is that the variable names can be used in the body of the let however in let* the initial value forms for each variable can refer to variables introduced earlier in the variables list. For more understanding please consider the following the example :
CL-USER>(let* ((a 10) (b (+ a 10))) (list a b))
CL-USER>(let ((a 10)) (let ((b (+ a 10))) (list a b)))
We can say let do everything in parallel but let* do sequential binding.
Increament & Decreament
There are different ways to increase or decrease a value in common lisp. First you can do this by “+” and “-”.
CL-USER>(setf x (+ x 1)) CL-USER>(setf x (- x 1))
However common lisp provided very easiest way to do that by using incf and decf. Please notice that the following examples are exactly the same as two example that is mentioned above.
CL-USER>(incf x) CL-USER>(decf x)