;; ------------------------------------------------------------ ;; GETTING STARTED ;; invoke this file either with clisp, or sbcl. Both are available ;; on zeus. add -q to minimize clisp's many-line splash screen. ;; sbcl is a faster implementation, which can be installed with many package ;; managers (e.g. homebrew on mac). Since it doesn't remember previous ;; instructions on its own, I'd recommend also installing rlwrap, a ;; handy tool that wraps around other commands to provide ;; previous-instruction history. I even went ahead and added (alias ;; clisp="clisp -q") and (alias sbcl="rlwrap sbcl") to my home machine's ;; .bash_profile so that each tool is used in my preferred way. ;; clisp -q ;; rlwrap sbcl ;; you can also immediately load a file from the command line for ;; either implementation: ;; ;; clisp -i code.lisp ;; sbcl --load code.lisp ;; you can also interactively load files by calling the load function: ;; (load "yourfile.lisp") ;; Notice that the implementations might have slightly different ;; CLI's (command line interfaces), but once you're writing code ;; in the interactive mode, the usage is the same (because that's ;; part of the actual language). ;; ------------------------------------------------------------ ;; DOCUMENTATION ;; "Common Lisp Hyperspec": http://clhs.lisp.se/ ;; ------------------------------------------------------------ ;; COMMENTS ; ; single-line comments begin with a semi-colon. Double ;;'s are a ; stylistic choice. #| multiline comments begin with #| and end with |# hey, look - they can be nested! |# ;; ------------------------------------------------------------ ;; QUITTING ; you can quit by calling (quit) or (exit) at the interactive loop. ;; mashing Ctrl-D may also help, but you could go too far and ;; terminate your shell process... ;; ------------------------------------------------------------ ;; debugger ;; if you mess things up, you might get dumped into the debugger. ;; typing one of the option numbers will select it, or you can ;; immediately escape out of it back to the interactive prompt. ;; in sbcl, type :toplevel ;; in clisp, type :a ;; ------------------------------------------------------------ ;; literal values evaluate to themselves. ;; strings are surrounded by double-quotes. "hello, world!" ;; characters are represented after #\ ;; (we won't do much of this...) #\g #\tab #\{ #\tab #\newline #\space #\backspace #\escape ;; integers 5 -123 0 ;; short- single- double- and long-precision floats 3.14s0 3.14f0 3.14d0 3.14l0 3.14e0 ; uses default precision of machine, likely double(64 bit) ;; rationals (fractions). Cool/weird! 2/3 ;; complex numbers... #c(3 4) ;; we don't specifically have booleans. But, we do have our stand-ins ;; for true and false: T for true, and NIL for false. (yes, NIL will ;; also be our empty list value later on.) T NIL ;; fun fact: the "Jargon File", recording slang terms used by ;; various computer hackers, includes T and NIL in them. Whether ;; this is because of LISP or ham radio may be up for debate. ;; http://catb.org/jargon/html/ ;; http://catb.org/jargon/html/N/NIL.html ;; incidentally, NIL will also be the empty list. A bit ;; unfortunate we won't be able to tell the difference. ;; string literals. They are not lists of characters, they are ; ;a distinct type. "hello" ;; ------------------------------------------------------------ ;; A list is parentheses around a symbol and 0 or more following ;; expressions (separated by whitespace). (+ 1 2 3) ;; if we don't want it to be treated like a reducible expression, we ;; put a quote before it to say "don't evaluate this chunk of AST." '(1 (+ 2 3) 4) ;; ===> '(1 (+ 2 3) 4) ;; alternatively, we can use the list function to say "build the list ;; with these expressions' results." It's not quite identical to the ;; leading quote above: it'll evaluate each expression inside. (list 1 (+ 2 3) 4) ;; ===> (1 5 4) ;; it turns out that there's no real barrier between our syntax and ;; internal representation. Thus the very spartan syntax, but also, we ;; get more direct access to the internal representation such as with ;; quoting expressions. We don't have to mess around with promoting or ;; demoting syntax to/from internal representation, as it's one and ;; the same! ;; ------------------------------------------------------------ #| many common operators exist. We apply them, and any functions, in prefix order, adjacent to their arguments, in parentheses. *** Parentheses always have a meaning, so don't just go adding more! *** In math, we might say: 1 + 2 * 3 + 4 In Lisp, we would say: (+ 1 (* 2 3) 4) |# (+ 2 3) (- 10 4) (* 6 7) (/ 42 5) ;; modulus/remainder, absolute value (mod 12 5) (mod -1 5) (rem 12 5) (rem -1 5) (abs -13) ;; max/min accept one or more arguments. (max -10 -13 -5) (min 6 8 4 5 12) ;; to chain multiple things together, we still have to use ;; prefix notation every time! 2*3 + 4 (+ (* 2 3) 4) ;; 2 + 3*4 ;; (+ 2 (* 3 4)) #| think of the tree! + |__ 2 | |__ * | |_ 3 |_ 4 |# (+ 2 (* 3 4)) ;; boolean operators: and (accepts many args), or (accepts many args), ;; not (unary). (and T T T) (and T T T T NIL T T) (or NIL NIL NIL T NIL) (not T) ;; relational operators (> 3 5) (>= 2 3) (< 2 3 4 5) ;; whoah, it checked (and (< 2 3) (< 3 4) (< 4 5)) (<= 5 6) ;; for primitive types, we can use =, but there's a bigger ;; story to tell about equality checks (see just below). (= 4 4) ;; concatenation is a bit messy. must mention the type first. (concatenate 'string "abc" "def" "ghi") (concatenate 'list '(1 2 3) '(4 5)) ;; for lists specifically, append is preferable. (append (list 1 2 3) (list 4 5)) ;; ------------------------------------------------------------ ;; since sub-expressions are just lists whose first item is an ;; operation, when we want to just leave a list value alone, we need ;; to escape it with a quote, or else lisp tries to use the first item ;; as an operation (and either fails or does something you didn't ;; want). We can also use the function named list to build and ;; output a list value. (list 1 2 3) '(1 2 3) ;; ------------------------------------------------------------ #| what's the meaning of various equality checks? = : only for numbers. converts freely between them to compare. eq : checks for them being the same object (same symbol). eql : allows = or eq. (it's like eq, but relaxed for characters and numbers (nums must be same type; 3 != 3.0)) equal: must be structurally similar (print out the same) - basically, if they are eql or make the same list equalp: also structurally similar, but can ignore type differences (unlike plain equal). (equalp 3 3.0) is true. |# ;; all true. (= 3 (+ 1 2)) ;; T (eq 3 (+ 1 2)) ;; T (eql 3 (+ 1 2)) ;; T (equal 3 (+ 1 2)) ;; T (equalp 3 (+ 1 2)) ;; T ;; = not allowed with non-numbers ;; (= "abc" (concatenate 'String "ab" "c")) (eq "abc" (concatenate 'String "ab" "c")) ;; NIL (eql "abc" (concatenate 'String "ab" "c")) ;; NIL (equal "abc" (concatenate 'String "ab" "c")) ;; T (equalp "abc" (concatenate 'String "ab" "c")) ;; T ;; simplified approach: use = for numbers, equal for everything else. ;; ------------------------------------------------------------ #| defining functions: (defun ( params go here without commas) "optional documentation string" bodyExpression ) It's like this in Haskell: -- optional documentation string funcName params go here without commas = bodyExpression |# (defun inc (x) "adds one to its argument" (+ x 1)) (inc 4) (inc 20) #| if-expressions: (if cond thenbranch elsebranch) of course we can nest 'em anywhere expressions are accepted. |# ;; this is parenthesized how we might initially want to, coming from ;; imperative languages, but look futher below to adjust to the normal ;; lisp style. (defun max3 (x y z) "chooses biggest of three" (if (and (>= x y) (>= x z) ) x (if (>= y z) y z ) ) ) ;; this just re-defines it... watch out! (shown again for slightly ;; more lispy parenthesis placement/indentation, with a big pile of ;; them at the ends of lines). (defun max3 (x y z) "chooses biggest of three" (if (and (>= x y) (>= x z)) x (if (>= y z) y z))) ;; re-re-defined; if-guard all on one line, this is the most lispy ;; layout of the three shown varieties. (defun max3 (x y z) "chooses biggest of three" (if (and (>= x y) (>= x z)) x (if (>= y z) y z))) ;; recursion is definitely gonna be our game. (defun fact (n) (if (<= n 1) 1 (* n (fact (- n 1))))) ;; just call itself with new arguments. ;; note: we can't do that last multiplication until the recursive call ;; returns. So we are building up a chain of pending multiplies on our ;; stack. #| Look at how we relate the loop version to the tail-recursive version below. python version: def fact(n): if n<=1: return 1 prod = n for i in range(n-1, 0, -1): prod *= i return prod |# ;; helper functions are again quite useful, especially as a new lisp ;; programmer. ;; helper function for factorial. (defun factorial-help (acc n) "tail-recursion friendly version of factorial." (if (zerop n) acc (factorial-help (* n acc) (- n 1)))) (defun factorial (n) "driver for fact (tail recursive)." (factorial-help 1 n)) ;; Note: SBCL automatically handles tail call optimization; clisp does ;; not. If you want to enable it in clisp, you need to manually tell ;; it that you'd like it to have a lower level of debugging ;; information than usual, so that it doesn't need to keep around ;; otherwise-useless frames for tracing purposes. You also need to ;; compile the code. (See the proclaim and compile usage below). You ;; shouldn't need it for any of the examples on our assignment, as ;; they do not dive so deeply in a recursive fashion. But it's fun to ;; see tail call optimization at work! ;; (proclaim '(optimize (debug 1))) ;; (compile 'factorial-help) ;; ------------------------------------------------------------ #| multiple if's? You could use cond. Feed it (expr expr) shapes, each being the condition to pass and the expression to use if that was the first true condition. You'll likely need to parenthesize both exprs if they're any actual calculations. You can choose to have a default "else" branch or not; just have a last condition-pair thing with T as the condition. What if you didn't have it? You will get back a NIL, which might be confused for the output of one of your previous pathways... (cond (boolExpr1 expr1) (boolExpr2 expr2) ... ) |# (defun max4 (a b c d) (cond ((and (>= a b) (>= a c) (>= a d)) a) ((and (>= b c) (>= b d)) b) ((>= c d) c) (T d) ;; true condition behaves as our "else" branch. ) ) ;; ------------------------------------------------------------ #| let-expressions: let us bind variables to values. NOTE: all RHS exprs are evaluated, and /then/ they all get assigned! Below, that means that expr1, expr2, etc. are calculated, and then we assign them to their respective variables - so if you're introducing var1 here, you can't use var1 in any of the exprs as it's too late: the expr got evaluated before var1 started existing. (if you want them performed sequentially to use each other, then you need the let* form). (let ( ;; or, use let* (var1 expr1) (var2 expr2) ... ) body ) |# ;; just a silly example... (defun max5 (a b c d e) (let ( (ab (max a b) ) (cd (max c d) ) ) (max3 ab cd e) ) ) ;; enforcing sequential bindings via let*. Wouldn't work with plain ;; let, as some of the RHS's would be using variables that don't yet ;; exist. (defun min5 (a b c d e) (let* ((v1 (min a b)) (v2 (min v1 c)) (v3 (min v2 d)) (v4 (min v3 e))) v4 )) ;; ------------------------------------------------------------ ;; LISTS (This is the LISt Processing language, after all!) ;; --- - ;; we can feed multiple values to list. (list 2 4 1 3 5) ;; we can quote something (') to avoid evaluating it, leaving it as an ;; expression until we need it later. here, some undeclared variables ;; are sneaking into our list value. Don't evaluate them here! '(a b c) ;; good thing that was quoted, because a, b, c don't exist in our ;; file as definitions. #| ------------------------------ Some List Functions and things ------------------------------ cons: accepts the head element and the tail (rest of the list). (cons expr expr) NIL: used to mean an empty list. car: deconstructs a cons and gives back the head element. (car expr) cdr: deconstructs a cons and gives back the tail list. (cdr epr) first/rest: identical to car/cdr. Less historically faithful. More sane to read. (first expr) (rest expr) |# ;; equivalent to (list 1 2 3): (cons 1 (cons 2 (cons 3 NIL))) (car '(10 15 20 25)) (cdr '(10 15 20 25)) (first (list 2 4 6)) (rest (list 2 4 6)) ;; ------------------------------------------------------------ ;; useful predicate functions. Often (but not always), named as ;; a claim and the letter p (for "predicate"). ;; predicate: is it zero? (zerop 0) (zerop 5) ;; predicate: is it a list that is empty? (null (list 1 2 3)) (null NIL) (null (list)) ;; predicate: are we looking at a cons cell? (consp NIL) ;; is NIL a cons cell? no. (consp (list 1 2 3)) ;; does this list begin with a cons? yep. ;; predicates: is this even? is that odd? (evenp 4) ;; even check (oddp 5) ;; odd check ;; ------------------------------------------------------------ ;; structural recursion on lists: step through head item per recursive ;; call. We don't have pattern matching directly like we use ;; in Haskell, so we use conditionals that check if we have some ;; specific shape, and then safely use accessor functions. It's ;; just like doing a null pointer check before using a pointer; ;; here, we check "is the list non-empty?", and if so, we're ;; able to safely call for the first item. (defun my-length (xs) (if (null xs) 0 (+ 1 (my-length (rest xs))))) (defun my-sum (xs) (if (null xs) 0 (+ (first xs) (my-sum (rest xs))))) #| in Haskell, as a comparison (written with the same algorithm, i.e. no pattern matching): mysum xs = if [] == xs then 0 else (head xs) + (mysum (tail xs)) and also written using pattern matching: mysum [] = 0 mysum (x:xs) = x + mysum xs |# ;; get a list of the evens that were in the input. (defun evens (xs) "docstring info here" (cond ((null xs) NIL) ((evenp (first xs)) (cons (first xs) (evens (rest xs)))) (T (evens (rest xs))))) (defun odds (xs) (cond ((null xs) NIL) ((oddp (first xs)) (let ((tail (odds (rest xs)))) (cons (first xs) (odds tail)))) (T (odds (rest xs))))) ;; accept optional argument(s) with &optional, and either give the ;; variable name or a name with a default: (defun addnums (x &optional (y 0) (z 0)) "adds up to three numbers" (+ x y z)) ;; &rest accepts zero or more arguments and packs them into a list ;; (which can be empty). ;; This is the same idea as Python's polyvariadic arguments, e.g. ;; def print (*args):... . (defun repackage (&rest xs) "boring function that groups the variable # of args into a list." xs) ;; ------------------------------------------------------------ ;; ------------------------------------------------------------ ;; APPLY ;; ------------------------------------------------------------ ;; apply takes a (quoted) function and a /LIST/ of arguments; it ;; unpacks that list into positional arguments and calls the function. (defun my-max (x &rest xs) "" (if (null xs) x (let ((xsmax (apply 'my-max xs))) (if (> x xsmax) x xsmax)))) ;; NOTE: we can have variables and functions that are the same name - ;; confusing! If we forget the quote before 'my-max above, then lisp ;; complains that "variable MY-MAX has no value". Because there is no ;; such variable, despite there being a function by that name. ;; ------------------------------------------------------------ ;; tracing! turn on tracing of a function to see all its calls, ;; no matter who called it (you or another function call). #| (trace my-length) (my-length (list 1 2 3 4 5)) (untrace my-length) |# ;; trace all the relevant functions when debugging, in batches. #| (trace my-length my-sum) (untrace my-length my-sum) |# ;; all this tracing will muddy up the loading of this file... ;; don't worry about it. ;; ------------------------------------------------------------ ;; dribbling: record this portion of all terminal output. ;; (but outputs from tracing is only available in the dribble ;; output when you are in CLISP; sbcl only captures the normal ;; console I/O). #| ;; this comment block should be active to see what's happening. ;; for some reason, sbcl doesn't seem to play as nicely with ;; this as clisp - I'm getting the toplevel commands/responses, ;; but not the traces. ;; SO, run these commands yourself to play with tracing and ;; dribbling to a file; choose some other unique name perhaps ;; for your output file. (trace my-sum my-length) (dribble "thisfile.txt") ;; do some stuff... (my-length (list 1 2 3)) (my-sum '(1 2 3 4 5)) (untrace my-sum my-length) ;; turn off dribbling, save/close the file. (dribble) |# ;; ------------------------------------------------------------ ;; first-class functions: here, our parameter p is expected to be ;; callable. So we (funcall p args go here). ;; ------------------------------------------------------------ ;; ------------------------------------------------------------ ;; FUNCALL ;; ;; ------------------------------------------------------------ ;; notice we must use (funcall p ), not just (p ). ;; function arguments aren't specially designated, so we help ;; Lisp know they should be called. (defun filter (p xs) (if (null xs) NIL (if (funcall p (first xs)) (cons (first xs) (filter p (rest xs))) ( filter p (rest xs))))) ;; we don't want evenp to be evaluated here - in order to pass ;; it in to filter untouched, we have to 'quote it as we've learned. (filter 'evenp (list 1 2 3 4 5 6)) #| in haskell: filter p [] = [] filter p (x:xs) | p x = x : filter p xs | True = filter p xs |# ;; including the "sharp-quote" #' means we ask the byte-compiler to ;; check that there really is a definition named evenp at compile ;; time, not at runtime. Mostly you won't experience much difference ;; one way or the other. (#' vs plain old ' by itself). (filter #'evenp (list 1 2 3 4 5 6)) ;; note that a variable is distinct from a function! p was a variable ;; above, so (funcall p ...) would cause us to lookup p, find the ;; function value associated with it, and carry on; but if we wanted ;; to just call a function directly by name we need to quote it: (funcall 'my-length '(1 2 3)) ;; ------------------------------------------------------------ #| lambdas: we can build up a function in-place: (lambda (params here) body) |# (defun FIVE-UP (x) (> x 5)) ;; let's find all the numbers above 5 in the list 3->8. (filter (lambda (x) (> x 5)) (list 3 4 5 6 7 8)) ;; ------------------------------------------------------------ ;; first-order functions: to pass a function as an argument, we have ;; to put ' (or #') in front of it. This is a macro that expands to ;; create (function
) ;; mapcar: given a function and a list, apply the function to each ;; item in the list, and put these results into a new list that we get ;; back. (often called map in other languages). (mapcar #'inc (list 10 11 12)) (mapcar (lambda (x) (* x 4)) (list 10 11 12)) (defun mapp (f xs) "let's build a map." (if (null xs) nil (cons (funcall f (first xs)) (mapp f (rest xs))))) ;; maplist: keep applying the given function to each successive tail ;; list. Kindof odd... (maplist #'my-sum (list 4 3 2 1)) ;; ------------------------------------------------------------ ;; using "pairs" (listy things that don't actually have a list in ;; the second position). (defun zipp (xs ys) (cond ((null xs) nil) ((null ys) nil) (T (cons (cons (first xs) (first ys)) (zipp (rest xs) (rest ys)))))) #| in Haskell: zip (x:xs) (y:ys) = (x,y) : (zip xs ys) zip _ _ = [] |# ;; ------------------------------------------------------------ ;; reduce: much like foldr in Haskell, this accepts a binary ;; function and a list of arguments. The function should accept ;; zero arguments and safely give back the identity value, ;; e.g. addition should give back zero, multiplication give ;; back 1, and so on. ;; http://clhs.lisp.se/Body/f_reduce.htm ;; has optional values that default to zero - important for ;; reduce to know how to terminate. (defun add2 ( &optional (x 0) (y 0)) (+ x y)) (defun my-add (&rest vals) (cond ((null vals) 0) ;; <==== this zero is our base case value for reducing ((= 1 (length vals)) (first vals)) ((= 2 (length vals)) (+ (first vals) (cadr vals))) (T (+ (first vals) (apply 'my-add (rest vals)))))) ;; these reduce calls all work. (reduce #'add2 '(1 2 3 4 5)) (reduce #'add2 '(1 2 3)) (reduce #'add2 '(1)) (reduce #'add2 '()) (reduce #'my-add '(1 2 3 4 5)) (reduce #'my-add '(1 2 3)) (reduce #'my-add '(1)) (reduce #'my-add '()) (defun add-perhaps-two (x &rest vals) (cond ((null vals) x) (T (+ x (apply 'add-perhaps-two vals))))) (defun add-stuff (&rest vals) (cond ((null vals) 0) (T (+ (first vals) (apply 'add-stuff (rest vals)))))) ;; built-in functions can also be used. Again, with #' usage. (reduce '* '(1 2 3 4 5)) ;; ------------------------------------------------------------ #| Python version: def min-max(xs): if len(xs)==0: return None min = xs[0] max = xs[0] for i in range(len(xs)): if (min>xs[i]): min = xs[i] if (max a b) a b)) ;; if we wanted to make one pass over the list and find the minimum ;; and maximum value in the list, we'd need to keep track of both ;; values while walking along the list. That means we need an extra ;; parameter for each. (defun min-max-helper (minval maxval xs) ;; if the list is empty, give back our current best knowledge of ;; minimum and maximum values. (if (null xs) (list minval maxval) ;; when the list wasn't empty, let's make these bindings: (let* ( (x (first xs)) ;; head of the list (therest (rest xs)) ;; tail of the list (minval (choose-smaller minval x)) ;; smaller of x and our current smallest-seen value (maxval (choose-bigger maxval x)) ;; larger of x and our current largest-seen value ) ;; recursively call ourselves with the updated min/max values, ;; on the tail of the list. (we're done with the value at the ;; head of this current list node). (min-max-helper minval maxval therest)))) ;; this is our entire goal. It relies upon the helper above. (defun min-max (xs) (if (null xs) NIL (min-max-helper (first xs) (first xs) (rest xs)))) ;; ------------------------------------------------------------ ;; "special global" values ;; declares *foo* to have the value 5. we didn't have to include the ;; *'s in the name, but it's convention. (defvar *foo* 5) (defparameter *bar* 10) ;; defvars can't be changed... so just use defparameter. (defvar *foo* 15) ;; still 5! (defparameter *bar* 20) ;; changes to 20. ;; as usual, don't get carried away with globals. Skip 'em entirely on ;; the homework! Other than sample testing data you want to have around. ;; ------------------------------------------------------------ ;; sequencing: if you do printing (print expr), or create variables, ;; or otherwise want to sequence other actions, we can always glue ;; together a few with progn. It evaluates them in turn, and then ;; returns the value of the last one. #| (progn (print "hello ") (print "world!") nil ) |# ;; this is more relevant with computations that use the stateful parts ;; of LISP, which we've somewhat ignored to keep/focus on the ;; functional flavor of the language. ;; ------------------------------------------------------------ ;; USING MACROS ;; here are some examples of more macros in Lisp. But when they get ;; away from the functional feel, we will de-emphasize them in our ;; course. ;; by the way: if, cond, defun, setf are all macros too. Anything that ;; doesn't treat the items in the list like a normal function call ;; will end up being some macro. #| ;; commented out because it prints when the file is loaded... (dotimes (i 6) (print i)) (dolist (v '(2 4 6 5 3 1)) (print v)) ;; one or more forms that are done in order (when (> 5 3) ;; one conditional... (print "using the") ;; as many more forms as we want. (print "when-macro.") ;; like this extra one. ) (unless (> 3 5) (print "hello")) ;; there is also do, and loop, which have their own little ecosystems of a mini-language all wrapped up in them. Let's skip for our brief introduction. |# ;; ------------------------------------------------------------ ;; we might not need this much in our current classwork since we're ;; trying not to write particularly imperative code while experiencing ;; LISP. ;; ------------------------------------------------------------ ;; MACROS: we can define new _syntax_ in our language! Don't think of ;; other languages' use of the word "macro". We're just showing what ;; pattern of stuff will be fed to this function-like thing, and then ;; how to deconstruct it. Since all our programs are basically lists ;; (they're all parenthesized things), it's really trivial to create ;; macros. #| ;; taken from: ;; http://www.gigamonkeys.com/book/macros-standard-control-constructs.html (defmacro when (condition &rest body) `(if ,condition (progn ,@body))) (defmacro unless (condition &rest body) `(if (not ,condition) (progn ,@body))) ...but these are already in the language as special operators, so we can't define them on our own again. meanings of those operators: ' the forward-quote means to stop evaluation in the following expression. ` the back-quote means to stop evaluation in that expression too, but any comma-prefixed expression inside will be evaluated. , the comma indicates that we want to evaluate this expression inside of a back-quoted expression. ,@ the comma-at also indicates to evaluate this expression inside of a back-quoted expression (which *must* be a list), but it splices the resulting list's items inline rather than the list itself. (Think of it as "the apply version of comma"). '(1 (+ 2 3) 4) ;; (1 (+ 2 3) 4) `(1 (+ 2 3) 4) ;; (1 (+ 2 3) 4) `(1 ,(+ 2 3) 4) ;; (1 5 4) `(1 (list 2 3) 4) ;; (1 (LIST 2 3) 4) `(1 ,(list 2 3) 4) ;; (1 (2 3) 4) `(1 ,@(list 2 3) 4) ;; (1 2 3 4) |# #| (when (> 5 3) ;; one conditional... (print "using the") ;; as many more forms as we want. (print "when-macro.") ;; like this extra one. ) |# ;; some other macros: ;; AND, OR, COND, DOLIST, DOTIMES, LOOP (defmacro whenn (condition &rest body) `(if ,condition (progn ,@body))) (defmacro myIF (guard elsebranch thenbranch) (list 'if guard thenbranch elsebranch)) ;; completely pointless 3-way harcoded branch (defmacro ifff (guard1 guard2 b1 b2 b3) (list 'if guard1 b1 (list 'if guard2 b2 b3))) ;; usage: (defun stuff (x) (ifff (> x 100) (> x 10) "big" "medium" "not big")) (stuff 5) (stuff 50) (stuff 500) ;; (setf a 13) ;; (ifff (= 0 a) (> a 0) (print "zero") (print "positive") (print "negative")) ;; (setf a 0) ;; (ifff (= 0 a) (> a 0) (print "zero") (print "positive") (print "negative")) ;; (setf a -13) ;; (ifff (= 0 a) (> a 0) (print "zero") (print "positive") (print "negative")) ;; ------------------------------------------------------------