koans, stuff
dice - more practice with signal and clog backquote - first practice with @,a splicing macros - some news of common errors: - capturing outer symbols, so caller expects them to be used, but invisible internal values take their place - evaluating "pass by name" forms too many times - evaluating them in surprising order
This commit is contained in:
@@ -23,19 +23,22 @@
|
||||
(cond ((null forms) 'nil)
|
||||
((null (rest forms)) (first forms))
|
||||
(t `(when ,(first forms)
|
||||
,(generate (rest forms)))))))
|
||||
,(generate (rest forms))))))) ; wowy
|
||||
(generate forms)))
|
||||
|
||||
(when (= 2 3) "hello")
|
||||
|
||||
(define-test my-and
|
||||
;; ASSERT-EXPANDS macroexpands the first form once and checks if it is equal
|
||||
;; to the second form.
|
||||
(assert-expands (my-and (= 0 (random 6)) (error "Bang!"))
|
||||
'(when (= 0 (random 6)) (error "Bang!")))
|
||||
;; ASSERT-EXPANDS macroexpands the first form once and checks if it is equal
|
||||
;; to the second form.
|
||||
(assert-expands (my-and (= 0 (random 6)) (error "Bang!"))
|
||||
'(when (= 0 (random 6)) (error "Bang!")))
|
||||
(assert-expands (my-and (= 0 (random 6))
|
||||
(= 0 (random 6))
|
||||
(= 0 (random 6))
|
||||
(error "Bang!"))
|
||||
____))
|
||||
'(when (= 0 (random 6))
|
||||
(when (= 0 (random 6)) (when (= 0 (random 6)) (error "Bang!"))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
@@ -43,16 +46,19 @@
|
||||
|
||||
(define-test variable-capture
|
||||
(macrolet ((for ((var start stop) &body body)
|
||||
`(do ((,var ,start (1+ ,var))
|
||||
(limit ,stop))
|
||||
((> ,var limit))
|
||||
,@body)))
|
||||
`(do ((,var ,start (1+ ,var))
|
||||
(limit ,stop))
|
||||
((> ,var limit))
|
||||
,@body)))
|
||||
(let ((limit 10)
|
||||
(result '()))
|
||||
(for (i 0 3)
|
||||
(push i result)
|
||||
(assert-equal ____ limit))
|
||||
(assert-equal ____ (nreverse result)))))
|
||||
(push i result)
|
||||
(assert-equal 3 limit))
|
||||
(assert-equal '(0 1 2 3) (nreverse result))))) ; didn't get it on first tries, ugh
|
||||
;; oh, ok - then try to use names that wouldn't happen in outside condext
|
||||
;; so that explicitly defined things in outside context get overshadowed
|
||||
;; ok
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
@@ -60,20 +66,23 @@
|
||||
;;; meant to be evaluated once.
|
||||
|
||||
(define-test multiple-evaluation
|
||||
;; We use MACROLET for defining a local macro.
|
||||
(macrolet ((for ((var start stop) &body body)
|
||||
`(do ((,var ,start (1+ ,var)))
|
||||
((> ,var ,stop))
|
||||
,@body)))
|
||||
(let ((side-effects '())
|
||||
(result '()))
|
||||
;; Our functions RETURN-0 and RETURN-3 have side effects.
|
||||
(flet ((return-0 () (push 0 side-effects) 0)
|
||||
(return-3 () (push 3 side-effects) 3))
|
||||
(for (i (return-0) (return-3))
|
||||
(push i result)))
|
||||
(assert-equal ____ (nreverse result))
|
||||
(assert-equal ____ (nreverse side-effects)))))
|
||||
;; We use MACROLET for defining a local macro.
|
||||
(macrolet ((for ((var start stop) &body body)
|
||||
`(do ((,var ,start (1+ ,var)))
|
||||
((> ,var ,stop))
|
||||
,@body)))
|
||||
(let ((side-effects '())
|
||||
(result '()))
|
||||
;; Our functions RETURN-0 and RETURN-3 have side effects.
|
||||
(flet ((return-0 () (push 0 side-effects) 0)
|
||||
(return-3 () (push 3 side-effects) 3))
|
||||
(for (i (return-0) (return-3))
|
||||
(push i result)))
|
||||
(assert-equal '(0 1 2 3) (nreverse result))
|
||||
(assert-equal '(0 3 3 3 3 3) (nreverse side-effects)))))
|
||||
; omg, fuck this guessing
|
||||
; ok, the ,stop was evaluated on each iteraction
|
||||
; to check "whether to stop"
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
@@ -81,36 +90,53 @@
|
||||
;;; subforms.
|
||||
|
||||
(define-test wrong-evaluation-order
|
||||
(macrolet ((for ((var start stop) &body body)
|
||||
;; The function GENSYM creates GENerated SYMbols, guaranteed to
|
||||
;; be unique in the whole Lisp system. Because of that, they
|
||||
;; cannot capture other symbols, preventing variable capture.
|
||||
(let ((limit (gensym "LIMIT")))
|
||||
`(do ((,limit ,stop)
|
||||
(,var ,start (1+ ,var)))
|
||||
((> ,var ,limit))
|
||||
,@body))))
|
||||
(let ((side-effects '())
|
||||
(result '()))
|
||||
(flet ((return-0 () (push 0 side-effects) 0)
|
||||
(return-3 () (push 3 side-effects) 3))
|
||||
(for (i (return-0) (return-3))
|
||||
(push i result)))
|
||||
(assert-equal ____ (nreverse result))
|
||||
(assert-equal ____ (nreverse side-effects)))))
|
||||
(macrolet ((for ((var start stop) &body body)
|
||||
;; The function GENSYM creates GENerated SYMbols, guaranteed to
|
||||
;; be unique in the whole Lisp system. Because of that, they
|
||||
;; cannot capture other symbols, preventing variable capture.
|
||||
(let ((limit (gensym "LIMIT")))
|
||||
`(do ((,limit ,stop)
|
||||
(,var ,start (1+ ,var)))
|
||||
((> ,var ,limit))
|
||||
,@body))))
|
||||
(let ((side-effects '())
|
||||
(result '()))
|
||||
(flet ((return-0 () (push 0 side-effects) 0)
|
||||
(return-3 () (push 3 side-effects) 3))
|
||||
(for (i (return-0) (return-3))
|
||||
(push i result)))
|
||||
(assert-equal '(0 1 2 3) (nreverse result))
|
||||
(assert-equal '(3 0) (nreverse side-effects)))))
|
||||
;; didn't got on first try,
|
||||
;; but yes, for gensym limit ,stop is evaluated first
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define-test for
|
||||
(macrolet ((for ((var start stop) &body body)
|
||||
;; Fill in the blank with a correct FOR macroexpansion that is
|
||||
;; not affected by the three macro pitfalls mentioned above.
|
||||
____))
|
||||
(let ((side-effects '())
|
||||
(result '()))
|
||||
(flet ((return-0 () (push 0 side-effects) 0)
|
||||
(return-3 () (push 3 side-effects) 3))
|
||||
(for (i (return-0) (return-3))
|
||||
(push i result)))
|
||||
(assert-equal '(0 1 2 3) (nreverse result))
|
||||
(assert-equal '(0 3) (nreverse side-effects)))))
|
||||
(macrolet ((for ((var start stop) &body body)
|
||||
;; Fill in the blank with a correct FOR macroexpansion that is
|
||||
;; not affected by the three macro pitfalls mentioned above.
|
||||
(let ((initial (gensym "INITIAL"))
|
||||
(limit (gensym "LIMIT")))
|
||||
`(do* ((,initial ,start)
|
||||
(,limit ,stop)
|
||||
(,var ,initial (1+ ,var)))
|
||||
((> ,var ,limit))
|
||||
,@body))))
|
||||
(let ((side-effects '())
|
||||
(result '()))
|
||||
(flet ((return-0 () (push 0 side-effects) 0)
|
||||
(return-3 () (push 3 side-effects) 3))
|
||||
(for (i (return-0) (return-3))
|
||||
(push i result)))
|
||||
(assert-equal '(0 1 2 3) (nreverse result))
|
||||
(assert-equal '(0 3) (nreverse side-effects)))))
|
||||
|
||||
;; (do* ((a (return-0))
|
||||
;; (b (return-3))
|
||||
;; (i a (1+ i)))
|
||||
;; ((> i b))
|
||||
;; (push i result))
|
||||
;;
|
||||
;; so, my mistake was: using DO and trying to cross reference temp vars
|
||||
;; and mistyping 1+ as 1_
|
||||
|
||||
Reference in New Issue
Block a user