buch of things to remind myself

- characters are # and non-excape, #\a
- i am way to quick to judge pairwise operations from left with #'reduce
- iteration can be actually simple with dotimes, dolist.
  i'm quite afraid of #'loop but #'do seems cool and inviting
This commit is contained in:
efim 2022-07-30 18:33:04 +00:00
parent e076e76575
commit 9c64cf128f
4 changed files with 121 additions and 99 deletions

View File

@ -22,11 +22,12 @@
(dolist (number numbers)
;; (INCF PLACE N) is equivalent to (SETF PLACE (+ N PLACE)).
(incf sum number))
(assert-equal ____ sum))
(assert-equal 108 sum))
;; DOLIST can optionally return a value.
(let ((sum 0))
(assert-equal ____ (dolist (number numbers sum)
(assert-equal 108 (dolist (number numbers sum)
(incf sum number))))))
;; (apply #'+ '(4 8 15 16 23 42))
(define-test dotimes
;; The macro DOTIMES binds a variable to subsequent integers from 0 to
@ -34,10 +35,10 @@
(let ((stack '()))
(dotimes (i 5)
(push i stack))
(assert-equal ____ stack))
(assert-equal '(4 3 2 1 0) stack))
;; DOTIMES can optionally return a value.
(let ((stack '()))
(assert-equal ____ (dotimes (i 5 stack)
(assert-equal '(4 3 2 1 0) (dotimes (i 5 stack)
(push i stack)))))
(define-test do
@ -47,7 +48,7 @@
(do ((i 0 (1+ i)))
((> i 5))
(push i result))
(assert-equal ____ result))
(assert-equal '(5 4 3 2 1 0) result))
;; The epilogue of DO can return a value.
(let ((result (do ((i 0 (1+ i))
;; A variable bound by DO noes not need to be updated on
@ -55,7 +56,7 @@
(result '()))
((> i 5) (nreverse result))
(push i result))))
(assert-equal ____ result)))
(assert-equal '(0 1 2 3 4 5) result)))
(define-test loop-basic-form
;; The macro LOOP in its simple form loops forever. It is possible to stop the
@ -64,10 +65,10 @@
(loop (incf counter)
(when (>= counter 100)
(return counter)))
(assert-equal ____ counter))
(assert-equal 100 counter))
;; The RETURN special form can return a value out of a LOOP.
(let ((counter 0))
(assert-equal ____ (loop (incf counter)
(assert-equal 100 (loop (incf counter)
(when (>= counter 100)
(return counter)))))
;; The extended form of LOOP will be contemplated in a future koan.

View File

@ -19,79 +19,96 @@
;; Inside MAPCAR, he function 1+ will be applied to each element of NUMBERS.
;; A new list will be collected from the results.
(assert-equal '(2 3 4 5 6 7) (mapcar #'1+ numbers))
(assert-equal ____ (mapcar #'- numbers))
(assert-equal ____ (mapcar #'list numbers))
(assert-equal ____ (mapcar #'evenp numbers))
(assert-equal ____ (mapcar #'numberp numbers))
(assert-equal ____ (mapcar #'stringp numbers))
(assert-equal '(-1 -2 -3 -4 -5 -6) (mapcar #'- numbers))
(assert-equal '((1) (2) (3) (4) (5) (6)) (mapcar #'list numbers))
(assert-equal '(nil t nil t nil t) (mapcar #'evenp numbers))
(assert-equal '(t t t t t t) (mapcar #'numberp numbers))
(assert-equal '(nil nil nil nil nil nil ) (mapcar #'stringp numbers))
;; MAPCAR can work on multiple lists. The function will receive one argument
;; from each list.
(let ((other-numbers '(4 8 15 16 23 42)))
(assert-equal ____ (mapcar #'+ numbers other-numbers))
(assert-equal ____ (mapcar #'* numbers other-numbers))
(assert-equal '(5 10 18 20 28 48) (mapcar #'+ numbers other-numbers))
(assert-equal `(4 16 45 ,(* 16 4) ,(* 23 5) ,(* 42 6) ) (mapcar #'* numbers other-numbers))
;; The function MOD performs modulo division.
(assert-equal ____ (mapcar #'mod other-numbers numbers)))))
(assert-equal `(0 0 0 0 3 0) (mapcar #'mod other-numbers numbers)))))
(define-test mapcar-lambda
;; MAPCAR is often used with anonymous functions.
(let ((numbers '(8 21 152 37 403 14 7 -34)))
(assert-equal ____ (mapcar (lambda (x) (mod x 10)) numbers)))
(assert-equal '(8 1 2 7 3 4 7 6) (mapcar (lambda (x) (mod x 10)) numbers)))
(let ((strings '("Mary had a little lamb"
"Old McDonald had a farm"
"Happy birthday to you")))
(assert-equal ____ (mapcar (lambda (x) (subseq x 4 12)) strings))))
(assert-equal '(" had a l" "McDonald" "y birthd") (mapcar (lambda (x) (subseq x 4 12)) strings))))
(define-test map
;; MAP is a variant of MAPCAR that works on any sequences.
;; It allows to specify the type of the resulting sequence.
(let ((string "lorem ipsum"))
(assert-equal ____ (map 'string #'char-upcase string))
(assert-equal ____ (map 'list #'char-upcase string))
(assert-equal "LOREM IPSUM" (map 'string #'char-upcase string))
(assert-equal '(#\L #\O #\R #\E #\M #\ #\I #\P #\S #\U #\M) (map 'list #'char-upcase string))
;; Not all vectors containing characters are strings.
(assert-equalp ____ (map '(vector t) #'char-upcase string))))
(assert-equalp #(#\L #\O #\R #\E #\M #\ #\I #\P #\S #\U #\M) (map '(vector t) #'char-upcase string))))
;; have no idea what's with the T
(vector 1 2 3)
#(1 2 3 4)
(define-test transposition
;; MAPCAR gives the function as many arguments as there are lists.
(flet ((transpose (lists) (apply #'mapcar ____ lists)))
(let ((list '((1 2 3)
(4 5 6)
(7 8 9)))
(transposed-list '((1 4 7)
(2 5 8)
(3 6 9))))
(assert-equal transposed-list (transpose list))
(assert-equal ____ (transpose (transpose list))))
(assert-equal ____ (transpose '(("these" "making")
("pretzels" "me")
("are" "thirsty"))))))
;; MAPCAR gives the function as many arguments as there are lists.
(flet ((transpose (lists) (apply #'mapcar #'list lists)))
(let ((list '((1 2 3)
(4 5 6)
(7 8 9)))
(transposed-list '((1 4 7)
(2 5 8)
(3 6 9))))
(assert-equal transposed-list (transpose list))
(assert-equal list (transpose (transpose list))))
(assert-equal '(("these" "pretzels" "are")
("making" "me" "thirsty")) (transpose '(("these" "making")
("pretzels" "me")
("are" "thirsty"))))))
(mapcar #'list (mapcar #'list '(1 2 3) '(11 22 33) '(111 222 333)))
;; welp, that wraps result in list
(defun my-transpose (&rest lists)
(apply #'mapcar #'list lists))
(my-transpose (my-transpose '(1 2 3) '(11 22 33) '(111 222 333)))
(defun my-transpose2 (lists)
(apply #'mapcar #'list lists))
(my-transpose2 (my-transpose2 '((1 2 3) (11 22 33) (111 222 333))))
(define-test reduce
;; The function REDUCE combines the elements of a list by applying a binary
;; function to the elements of a sequence from left to right.
(assert-equal 15 (reduce #'+ '(1 2 3 4 5)))
(assert-equal ____ (reduce #'+ '(1 2 3 4)))
(assert-equal ____ (reduce #'expt '(1 2 3 4 5))))
;; The function REDUCE combines the elements of a list by applying a binary
;; function to the elements of a sequence from left to right.
(assert-equal 15 (reduce #'+ '(1 2 3 4 5)))
(assert-equal 10 (reduce #'+ '(1 2 3 4)))
(assert-equal 1 (reduce #'expt '(1 2 3 4 5))))
(reduce #'expt '(1 2 3))
(define-test reduce-from-end
;; The :FROM-END keyword argument can be used to reduce from right to left.
(let ((numbers '(1 2 3 4 5)))
(assert-equal ____ (reduce #'cons numbers))
(assert-equal ____ (reduce #'cons numbers :from-end t)))
;; The :FROM-END keyword argument can be used to reduce from right to left.
(let ((numbers '(1 2 3 4 5)))
(assert-equal '((((1 . 2) . 3) . 4) . 5) (reduce #'cons numbers)) ; and this I failed at first try
(assert-equal '(1 . (2 . (3 . (4 . 5)))) (reduce #'cons numbers :from-end t)))
(let ((numbers '(2 3 2)))
(assert-equal ____ (reduce #'expt numbers))
(assert-equal ____ (reduce #'expt numbers :from-end t))))
(assert-equal (expt (expt 2 3) 2) (reduce #'expt numbers))
(assert-equal (expt 2 9) (reduce #'expt numbers :from-end t)))) ; this I failed at first try
(define-test reduce-initial-value
;; :INITIAL-VALUE can supply the initial value for the reduction.
(let ((numbers '(1 2 3 4 5)))
(assert-equal ____ (reduce #'* numbers))
(assert-equal ____ (reduce #'* numbers :initial-value 0))
(assert-equal ____ (reduce #'* numbers :initial-value -1))))
(assert-equal 120 (reduce #'* numbers))
(assert-equal 0 (reduce #'* numbers :initial-value 0))
(assert-equal -120 (reduce #'* numbers :initial-value -1))))
(define-test inner-product
;; MAPCAR and REDUCE are powerful when used together.
;; Fill in the blanks to produce a local function that computes an inner
;; product of two vectors.
(flet ((inner-product (x y) (reduce ____ (mapcar ____ x y))))
(flet ((inner-product (x y) (reduce #'+ (mapcar #'* x y))))
(assert-equal 32 (inner-product '(1 2 3) '(4 5 6)))
(assert-equal 310 (inner-product '(10 20 30) '(4 3 7)))))

View File

@ -14,60 +14,64 @@
(define-test what-is-a-string
(let ((string "Do, or do not. There is no try."))
(true-or-false? ____ (typep string 'string))
(true-or-false? t (typep string 'string))
;; Strings are vectors of characters.
(true-or-false? ____ (typep string 'array))
(true-or-false? ____ (typep string 'vector))
(true-or-false? ____ (typep string '(vector character)))
(true-or-false? ____ (typep string 'integer))))
(true-or-false? t (typep string 'array))
(true-or-false? t (typep string 'vector))
(true-or-false? t (typep string '(vector character)))
(true-or-false? nil (typep string 'integer))))
(define-test multiline-string
;; A Lisp string can span multiple lines.
(let ((string "this is
a multi
line string"))
(true-or-false? ____ (typep string 'string))))
(true-or-false? t (typep string 'string))))
(define-test escapes-in-strings
;; Quotes and backslashes in Lisp strings must be escaped.
(let ((my-string "this string has one of these \" and a \\ in it"))
(true-or-false? ____ (typep my-string 'string))))
(true-or-false? t (typep my-string 'string))))
(let ((my-string "this string has one of these \" and a \\ in it"))
(format t "~&unsescaped string ~S" my-string)
(format t "~&escaped string ~A" my-string)) ; yay, that's how I remember it from the book
;; printing escaped characters for pretty text output
(define-test substrings
;; Since strings are sequences, it is possible to use SUBSEQ on them.
(let ((string "Lorem ipsum dolor sit amet"))
(assert-equal ____ (subseq string 12))
(assert-equal ____ (subseq string 6 11))
(assert-equal ____ (subseq string 1 5))))
;; Since strings are sequences, it is possible to use SUBSEQ on them.
(let ((string "Lorem ipsum dolor sit amet"))
(assert-equal "dolor sit amet" (subseq string 12))
(assert-equal "ipsum" (subseq string 6 11))
(assert-equal "orem" (subseq string 1 5))))
(define-test strings-versus-characters
;; Strings and characters have distinct types.
(true-or-false? ____ (typep #\a 'character))
(true-or-false? ____ (typep "A" 'character))
(true-or-false? ____ (typep #\a 'string))
(true-or-false? t (typep #\a 'character))
(true-or-false? nil (typep "A" 'character))
(true-or-false? nil (typep #\a 'string))
;; One can use both AREF and CHAR to refer to characters in a string.
(let ((my-string "Cookie Monster"))
(assert-equal ____ (char my-string 0))
(assert-equal ____ (char my-string 3))
(assert-equal ____ (aref my-string 7))))
(assert-equal #\C (char my-string 0))
(assert-equal #\k (char my-string 3))
(assert-equal #\M (aref my-string 7))))
(define-test concatenating-strings
;; Concatenating strings in Common Lisp is possible, if a little cumbersome.
(let ((a "Lorem")
(b "ipsum")
(c "dolor"))
(assert-equal ____ (concatenate 'string a " " b " " c))))
(assert-equal "Lorem ipsum dolor" (concatenate 'string a " " b " " c))))
(define-test searching-for-characters
;; The function POSITION can be used to find the first position of an element
;; in a sequence. If the element is not found, NIL is returned.
(assert-equal ____ (position #\b "abc"))
(assert-equal ____ (position #\c "abc"))
(assert-equal ____ (position #\d "abc")))
(assert-equal 1 (position #\b "abc"))
(assert-equal 2 (position #\c "abc"))
(assert-equal nil (position #\d "abc")))
(define-test finding-substrings
;; The function SEARCH can be used to search a sequence for subsequences.
(let ((title "A supposedly fun thing I'll never do again"))
(assert-equal ____ (search "supposedly" title))
(assert-equal 12 (search ____ title))))
(assert-equal 2 (search "supposedly" title))
(assert-equal 12 (search " fun" title))))

View File

@ -30,12 +30,12 @@
(define-test make-struct
(let ((player (make-basketball-player :name "Larry" :team :celtics
:number 33)))
(true-or-false? ____ (basketball-player-p player))
(assert-equal ____ (basketball-player-name player))
(assert-equal ____ (basketball-player-team player))
(assert-equal ____ (basketball-player-number player))
(true-or-false? t (basketball-player-p player))
(assert-equal "Larry" (basketball-player-name player))
(assert-equal :celtics (basketball-player-team player))
(assert-equal 33 (basketball-player-number player))
(setf (basketball-player-team player) :retired)
(assert-equal ____ (basketball-player-team player))))
(assert-equal :retired (basketball-player-team player))))
;;; Structure fields can have default values.
@ -46,8 +46,8 @@
(let ((player (make-baseball-player)))
;; We have not specified a default value for NAME, therefore we cannot
;; read it here - it would invoke undefined behaviour.
(assert-equal ____ (baseball-player-team player))
(assert-equal ____ (baseball-player-position player))))
(assert-equal :red-sox (baseball-player-team player))
(assert-equal :outfield (baseball-player-position player))))
;;; The accessor names can get pretty long. It's possible to specify a different
;;; prefix with the :CONC-NAME option.
@ -58,9 +58,9 @@
(define-test struct-access
(let ((player (make-american-football-player
:name "Drew Brees" :position :qb :team "Saints")))
(assert-equal ____ (nfl-guy-name player))
(assert-equal ____ (nfl-guy-team player))
(assert-equal ____ (nfl-guy-position player))))
(assert-equal "Drew Brees" (nfl-guy-name player))
(assert-equal "Saints" (nfl-guy-team player))
(assert-equal :qb (nfl-guy-position player))))
;;; Structs can be defined to include other structure definitions.
;;; This form of inheritance allows composition of objects.
@ -73,14 +73,14 @@
:start-year 2004 :end-year 2011
:name "Kobe Bryant"
:team :lakers :number 24)))
(assert-equal ____ (nba-contract-start-year contract))
(assert-equal ____ (type-of contract))
(assert-equal 2004 (nba-contract-start-year contract))
(assert-equal 'nba-contract (type-of contract))
;; Inherited structures follow the rules of type hierarchy.
(true-or-false? ____ (typep contract 'basketball-player))
(true-or-false? t (typep contract 'basketball-player))
;; One can access structure fields both with the structure's own accessors
;; and with the inherited accessors.
(assert-equal ____ (nba-contract-team contract))
(assert-equal ____ (basketball-player-team contract))))
(assert-equal :lakers (nba-contract-team contract))
(assert-equal :lakers (basketball-player-team contract))))
;;; Copying a structure named FOO is handled with the COPY-FOO function.
;;; All such copies are shallow.
@ -91,21 +91,21 @@
(manning-2 (make-american-football-player
:name "Manning" :team (list "Colts" "Broncos"))))
;; MANNING-1 and MANNING-2 are different objects...
(true-or-false? ____ (eq manning-1 manning-2))
(true-or-false? nil (eq manning-1 manning-2))
;;...but they contain the same information.
(true-or-false? ____ (equalp manning-1 manning-2))
(true-or-false? t (equalp manning-1 manning-2))
(let ((manning-3 (copy-american-football-player manning-1)))
(true-or-false? ____ (eq manning-1 manning-3))
(true-or-false? ____ (equalp manning-1 manning-3))
(true-or-false? nil (eq manning-1 manning-3))
(true-or-false? t (equalp manning-1 manning-3))
;; Setting the slot of one instance does not modify the others...
(setf (nfl-guy-name manning-1) "Rogers")
(true-or-false? ____ (string= (nfl-guy-name manning-1)
(true-or-false? nil (string= (nfl-guy-name manning-1)
(nfl-guy-name manning-3)))
(assert-equal ____ (nfl-guy-name manning-1))
(assert-equal ____ (nfl-guy-name manning-3))
(assert-equal "Rogers" (nfl-guy-name manning-1))
(assert-equal "Manning" (nfl-guy-name manning-3))
;; ...but modifying shared structure may affect other instances.
(setf (car (nfl-guy-team manning-1)) "Giants")
(true-or-false? ____ (string= (car (nfl-guy-team manning-1))
(true-or-false? t (string= (car (nfl-guy-team manning-1))
(car (nfl-guy-team manning-3))))
(assert-equal ____ (car (nfl-guy-team manning-1)))
(assert-equal ____ (car (nfl-guy-team manning-3))))))
(assert-equal "Giants" (car (nfl-guy-team manning-1)))
(assert-equal "Giants" (car (nfl-guy-team manning-3))))))