So i have this macro (basically a for loop):
(defmacro for ((parameter start-value end-value &optional (step 1)) &body e)
(let ((func-name (gensym))
(end (gensym)))
`(labels ((,func-name (,parameter ,end)
(if (<= ,parameter ,end)
(progn ,#e
(,func-name (+ ,parameter ,step) ,end)))))
(,func-name ,start-value ,end-value))))
And i want to test it with this:
(print (let ((j 0) (k 1))
(for (i 1 10 (incf k)) (print i))))
What i get now is:
1, 3, 6, 10, NIL.
which means that my step increments after each iteration, but i want it to increment only once in the beginning for this output:
1, 3, 5, 7, 9, NIL.
What's wrong with my macro and what should i do?
You need to compute the step value outside the loop:
CL-USER 12 > (defmacro for ((parameter start-value end-value
&optional (step 1))
&body e)
(let ((func-name (gensym))
(step-name (gensym))
(end (gensym)))
`(labels ((,func-name (,parameter ,end ,step-name)
(when (<= ,parameter ,end)
,#e
(,func-name (+ ,parameter ,step-name)
,end
,step-name))))
(,func-name ,start-value ,end-value ,step))))
FOR
CL-USER 13 > (pprint (macroexpand-1 '(for (i 1 10 (incf k)) (print i))))
(LABELS ((#:G954 (I #:G956 #:G955)
(WHEN (<= I #:G956) (PRINT I) (#:G954 (+ I #:G955) #:G956 #:G955))))
(#:G954 1 10 (INCF K)))
CL-USER 14 > (let ((j 0) (k 1))
(for (i 1 10 (incf k))
(print i)))
1
3
5
7
9
NIL
If you don't want to pass the step-value all the time, you need an outer LET binding its value.
Note: some Lisp implementations (many interpreters and some compilers) don't support TCO (tail call optimisation).
Related
I have this macro which is a "for" loop and it works great.
(defmacro for ((parameter start-value end-value
&optional (step 1))
&body e)
(let ((func-name (gensym))
(step-name (gensym))
(end (gensym)))
`(labels ((,func-name (,parameter ,end ,step-name)
(when (<= ,parameter ,end)
,#e
(,func-name (+ ,parameter ,step-name)
,end
,step-name))))
(,func-name ,start-value ,end-value ,step))))
But i need the result of my macro to be the value of the last expression in the body (of the body). Right now (in this code) the result is always nil. So what do i do?
Example for a single return value:
CL-USER 38 > (defmacro for ((parameter start-value end-value
&optional (step 1))
&body e)
(let ((func-name (gensym))
(step-name (gensym))
(end (gensym))
(last-name (gensym)))
`(labels ((,func-name (,parameter ,end ,step-name ,last-name)
(if (<= ,parameter ,end)
(,func-name (+ ,parameter ,step-name)
,end
,step-name
(progn ,#e))
,last-name)))
(,func-name ,start-value ,end-value ,step nil))))
FOR
CL-USER 39 > (let ((j 0) (k 1))
(for (i 1 10 (incf k))
(print i)))
1
3
5
7
9
9 ; the return value
I'm new to lisp, and have been trying to learn Common Lisp by diving in and writing some code. I've read plenty of documentation on the subject, but it's taking a while to really sink in.
I have written a couple of macros (? and ??) for performing unit tests, but I'm having some difficulty. The code is at the end of the post, to avoid cluttering the actual question.
Here is an example of usage:
(??
(? "Arithmetic tests"
(? "Addition"
(= (+ 1 2) 3)
(= (+ 1 2 3) 6)
(= (+ -1 -3) -4))))
And an example of output:
[Arithmetic tests]
[Addition]
(PASS) '(= (+ 1 2) 3)'
(PASS) '(= (+ 1 2 3) 6)'
(PASS) '(= (+ -1 -3) -4)'
Results: 3 tests passed, 0 tests failed
Now, the existing code works. Unfortunately, the (? ...) macro is ugly, verbose, resistant to change - and I'm pretty sure also badly structured. For example, do I really have to use a list to store pieces of output code and then emit the contents at the end?
I'd like to modify the macro to permit description strings (or symbols) to optionally follow each test, whereupon it would replace the test literal in the output, thus:
(??
(? "Arithmetic tests"
(? "Addition"
(= (+ 1 2) 3) "Adding 1 and 2 results in 3"
(= (+ 1 2 3) 6)
(= (+ -1 -3) -4))))
Output:
[Arithmetic tests]
[Addition]
(PASS) Adding 1 and 2 results in 3
(PASS) '(= (+ 1 2 3) 6)'
(PASS) '(= (+ -1 -3) -4)'
But unfortunately I can't find a sensible place in the macro to insert this change. Depending on where I put it, I get errors like you're not inside a backquote expression, label is not defined or body-forms is not defined. I know what these errors mean, but I can't find a way to avoid them.
Also, I'll be wanting to handle exceptions in the test, and treat that as a failure. Currently, there is no exception handling code - the test result is merely tested against nil. Again, it is not clear how I should add this functionality.
I'm thinking that maybe this macro is over-complex, due to my inexperience in writing macros; and perhaps if I simplify it, modification will be easier. I don't really want to separate it out into several smaller macros without good reason; but maybe there's a terser way to write it?
Can anyone help me out here, please?
A complete code listing follows:
(defmacro with-gensyms ((&rest names) &body body)
`(let ,(loop for n in names collect `(,n (gensym)))
,#body))
(defmacro while (condition &body body)
`(loop while ,condition do (progn ,#body)))
(defun flatten (L)
"Converts a list to single level."
(if (null L)
nil
(if (atom (first L))
(cons (first L) (flatten (rest L)))
(append (flatten (first L)) (flatten (rest L))))))
(defun starts-with-p (str1 str2)
"Determine whether `str1` starts with `str2`"
(let ((p (search str2 str1)))
(and p (= 0 p))))
(defmacro pop-first-char (string)
`(with-gensyms (c)
(if (> (length ,string) 0)
(progn
(setf c (schar ,string 0))
(if (> (length ,string) 1)
(setf ,string (subseq ,string 1))
(setf ,string ""))))
c))
(defmacro pop-chars (string count)
`(with-gensyms (result)
(setf result ())
(dotimes (index ,count)
(push (pop-first-char ,string) result))
result))
(defun format-ansi-codes (text)
(let ((result ()))
(while (> (length text) 0)
(cond
((starts-with-p text "\\e")
(push (code-char #o33) result)
(pop-chars text 2)
)
((starts-with-p text "\\r")
(push (code-char 13) result)
(pop-chars text 2)
)
(t (push (pop-first-char text) result))
))
(setf result (nreverse result))
(coerce result 'string)))
(defun kv-lookup (values key)
"Like getf, but works with 'keys as well as :keys, in both the list and the supplied key"
(setf key (if (typep key 'cons) (nth 1 key) key))
(while values
(let ((k (pop values)) (v (pop values)))
(setf k (if (typep k 'cons) (nth 1 k) k))
(if (eql (symbol-name key) (symbol-name k))
(return v)))))
(defun make-ansi-escape (ansi-name)
(let ((ansi-codes '( :normal "\\e[00m" :white "\\e[1;37m" :light-grey "\\e[0;37m" :dark-grey "\\e[1;30m"
:red "\\e[0;31m" :light-red "\\e[1;31m" :green "\\e[0;32m" :blue "\\e[1;34m" :dark-blue "\\e[1;34m"
:cyan "\\e[1;36m" :magenta "\\e[1;35m" :yellow "\\e[0;33m"
:bg-dark-grey "\\e[100m"
:bold "\\e[1m" :underline "\\e[4m"
:start-of-line "\\r" :clear-line "\\e[2K" :move-up "\\e[1A")))
(format-ansi-codes (kv-lookup ansi-codes ansi-name))
))
(defun format-ansi-escaped-arg (out-stream arg)
(cond
((typep arg 'symbol) (format out-stream "~a" (make-ansi-escape arg)))
((typep arg 'string) (format out-stream arg))
(t (format out-stream "~a" arg))
))
(defun format-ansi-escaped (out-stream &rest args)
(while args
(let ((arg (pop args)))
(if (typep arg 'list)
(let ((first-arg (eval (first arg))))
(format out-stream first-arg (second arg))
)
(format-ansi-escaped-arg out-stream arg)
))
))
(defmacro while-pop ((var sequence &optional result-form) &rest forms)
(with-gensyms (seq)
`(let (,var)
(progn
(do () ((not ,sequence))
(setf ,var (pop ,sequence))
(progn ,#forms))
,result-form))))
(defun report-start (form)
(format t "( ) '~a'~%" form))
(defun report-result (result form)
(format-ansi-escaped t "(" (if result :green :red) `("~:[FAIL~;PASS~]" ,result) :normal `(") '~a'~%" ,form))
result)
(defmacro ? (name &body body-forms)
"Run any number of test forms, optionally nested within further (?) calls, and print the results of each test"
(with-gensyms (result indent indent-string)
(if (not body-forms)
:empty
(progn
(setf result () indent 0 indent-string " ")
(cond
((typep (first body-forms) 'integer)
(setf indent (pop body-forms))))
`(progn
(format t "~v#{~A~:*~}" ,indent ,indent-string)
(format-ansi-escaped t "[" :white ,name :normal "]~%")
(with-gensyms (test-results)
(setf test-results ())
,(while-pop (body-form body-forms `(progn ,#(nreverse result)))
(cond
( (EQL (first body-form) '?)
(push `(progn
(setf test-results (append test-results (? ',(nth 1 body-form) ,(1+ indent) ,#(nthcdr 2 body-form))))
(format t "~%")
test-results
) result)
)
(t
(push `(progn
(format t "~v#{~A~:*~}" ,(1+ indent) ,indent-string)
(report-start ',body-form)
(with-gensyms (result label)
(setf result ,body-form)
(format-ansi-escaped t :move-up :start-of-line :clear-line)
(format t "~v#{~A~:*~}" ,(1+ indent) ,indent-string)
(push (report-result result ',body-form) test-results)
test-results
)) result))))))))))
(defun ?? (&rest results)
"Run any number of tests, and print a summary afterward"
(setf results (flatten results))
(format-ansi-escaped t "~&" :white "Results: " :green `("~a test~:p passed" ,(count t results)) :normal ", "
(if (find NIL results) :red :normal) `("~a test~:p failed" ,(count NIL results))
:yellow `("~[~:;, ~:*~a test~:p not run~]" ,(count :skip results))
:brown `("~[~:;, ~:*~a empty test group~:p skipped~]" ,(count :empty results))
:normal "~%"))
For my part, the ? macro is rather technical and it's hard to follow the logic behind the formatting functions. So instead of tracking errors I'd like to suggest my own attempt, perhaps it'll be of use.
I think that actually your ?? doesn't want to evaluate anything, but rather to treat its body as individual tests or sections. If the body includes a list starting with ?, this list represents a section; other elements are test forms optionally followed by descriptions. So in my implementation ?? will be a macro, and ? will be just a symbol.
I start with wishful thinking. I suppose I can create individual tests using a function make-test-item and test sections using a function make-test-section (their implementation is unimportant for now), that I can display them using an auxiliary function display-test and compute results using the function results, which returns two values: the total number of tests and the number of passed ones. Then I'd like the code
(??
(? "Arithmetic tests"
(? "Addition"
(= (+ 1 2) 3) "Adding 1 and 2 results in 3"
(= (+ 1 2 3) 6)
(= (+ -1 -3) 4))
(? "Subtraction"
(= (- 1 2) 1)))
(= (sin 0) 0) "Sine of 0 equals 0")
to expand into something like
(let ((tests (list (make-test-section :header "Arithmetic tests"
:items (list (make-test-section :header "Addition"
:items (list (make-test-item :form '(= (+ 1 2) 3)
:description "Adding 1 and 2 results in 3"
:passp (= (+ 1 2) 3))
(make-test-item :form '(= (+ 1 2 3) 6)
:passp (= (+ 1 2 3) 6))
(make-test-item :form '(= (+ -1 -3) 4)
:passp (= (+ -1 -3) 4))))
(make-test-section :header "Subtraction"
:items (list (make-test-item :form '(= (- 1 2) 1)
:passp (= (- 1 2) 1))))))
(make-test-item :form '(= (sin 0) 0)
:passp (= (sin 0) 0)
:description "Sine of 0 equals 0"))))
(loop for test in tests
with total = 0
with passed = 0
do (display-test test 0 t)
do (multiple-value-bind (ttl p) (results test)
(incf total ttl)
(incf passed p))
finally (display-result total passed t)))
Here a list of tests is created; then we traverse it printing each test (0 denotes the zero level of indentation and t is as in format) and keeping track of the results, finally displaying the total results. I don't think explicit eval is needed here.
It may not be the most exquisite piece of code ever, but it seems manageable. I supply missing definitions below, they are rather trivial (and can be improved) and have nothing to do with macros.
Now we pass on to the macros. Consider both pieces of code as data, then we want a list processing function which would turn the first one into the second. A few auxiliary functions would come in handy.
The major task is to parse the body of ?? and generate the list of test to go inside the let.
(defun test-item-form (form description)
`(make-test-item :form ',form :description ,description :passp ,form))
(defun test-section-form (header items)
`(make-test-section :header ,header :items (list ,#items)))
(defun parse-test (forms)
(let (new-forms)
(loop
(when (null forms)
(return (nreverse new-forms)))
(let ((f (pop forms)))
(cond ((and (listp f) (eq (first f) '?))
(push (test-section-form (second f) (parse-test (nthcdr 2 f))) new-forms))
((stringp (first forms))
(push (test-item-form f (pop forms)) new-forms))
(t (push (test-item-form f nil) new-forms)))))))
Here parse-test essentially absorbs the syntax of ??. Each iteration consumes one or two forms and collects corresponding make-... forms. The functions can be easily tested in REPL (and, of course, I did test them while writing).
Now the macro becomes quite simple:
(defmacro ?? (&body body)
`(let ((tests (list ,#(parse-test body))))
(loop for test in tests
with total = 0
with passed = 0
do (display-test test 0 t)
do (multiple-value-bind (ttl p) (results test)
(incf total ttl)
(incf passed p))
finally (display-result total passed t))))
It captures a few symbols, both in the variable name space and in the function one (the expansion may contain make-test-item and make-test-section). A clean solution with gensyms would be cumbersome, so I'd suggest just moving all the definitions in a separate package and exporting only ?? and ?.
For completeness, here is an implementation of the test API. Actually, it's what I started coding with and proceeded until I made sure the big let-form works; then I passed on to the macro part. This implementation is fairly sloppy; in particular, it doesn't support terminal colours and display-test can't even output a section into a string.
(defstruct test-item form description passp)
(defstruct test-section header items)
(defun results (test)
(etypecase test
(test-item (if (test-item-passp test)
(values 1 1)
(values 1 0)))
(test-section (let ((items-count 0)
(passed-count 0))
(dolist (i (test-section-items test) (values items-count passed-count))
(multiple-value-bind (i p) (results i)
(incf items-count i)
(incf passed-count p)))))))
(defparameter *test-indent* 2)
(defun display-test-item (i level stream)
(format stream "~V,0T~:[(FAIL)~;(PASS)~] ~:['~S'~;~:*~A~]~%"
(* level *test-indent*)
(test-item-passp i)
(test-item-description i)
(test-item-form i)))
(defun display-test-section-header (s level stream)
(format stream "~V,0T[~A]~%"
(* level *test-indent*)
(test-section-header s)))
(defun display-test (test level stream)
(etypecase test
(test-item (display-test-item test level stream))
(test-section
(display-test-section-header test level stream)
(dolist (i (test-section-items test))
(display-test i (1+ level) stream)))))
(defun display-result (total passed stream)
(format stream "Results: ~D test~:P passed, ~D test~:P failed.~%" passed (- total passed)))
All the code is licenced under WTFPL.
The function "greaterthan", (< NUM1 NUM2), allows only for returning t/nil for comparing 2 values.
I would like to test (var1 > var2 < var3 < var4), is there any way to do that using only one function in lisp? If not, what is the best procedure?
The best procedure is not to bother: (and (< var2 var1) (< var2 var3) (< var3 var4)) is not harder to read that your ..>..<..<.. chain.
It makes sense to test for the ascending order:
(require 'cl)
(defun cl-< (&rest args)
(every '< args (cdr args))
These days I don't hesitate to (require 'cl) anymore, but if you do,
here is another variant:
(defun cl-< (arg &rest more-args)
(or (null more-args)
(and (< arg (first more-args))
(apply #'cl-< more-args))))
The following is a macro implementation for variadic <
(defmacro << (x y &rest args)
(if args
(if (or (symbolp y)
(numberp y))
`(and (< ,x ,y) (<< ,y ,#args))
(let ((ys (make-symbol "y")))
`(let (,ys)
(and (< ,x (setq ,ys ,y))
(<< ,ys ,#args)))))
`(< ,x ,y)))
for simple cases just expands to (and ...) chains
(<< x y z) ==> (and (< x y) (< y z))
where the expression is not a number and not a symbol expands to a more complex form to avoid multiple evaluations in presence of side effects
(<< (f x) (g y) (h z)) ==> (let ((gy)) (and (< (f x) (setq gy (g y)))
(< gy (h z))))
for example
(setq foo (list))
nil
(defun call (x) (push x foo) x)
call
(<< (call 1) (call 2) (call 5) (call 4) (call 0))
nil
foo
(4 5 2 1)
every function has been called once, except for 0 that didn't need to be called because of short circuiting (I'm not 100% sure if short circuiting is a really good idea or not... #'< in Common Lisp is a regular function with all arguments all evaluated exactly once in left-to-right order without short circuiting).
(defun << (arg1 arg2 arg3 arg4)
(when (and (< arg1 arg2) (< arg2 arg3) (< arg3 arg4)))
)
(<< 1 2 3 4)
Probably possible to extend with any amount of arguments, but such a general form would seem useful.
(defmacro << (&rest args)
(let ((first (car args))
(min (gensym))
(max (gensym))
(forms '(t)) iterator)
(setq args (reverse (cdr args))
iterator args)
`(let ((,min ,first) ,max)
,(or
(while iterator
(push `(setq ,min ,max) forms)
(push `(< ,min ,max) forms)
(push `(setq ,max ,(car iterator)) forms)
(setq iterator (cdr iterator))) `(and ,#forms)))))
(macroexpand '(<< 10 20 30 (+ 30 3) (* 10 4)))
(let ((G99730 10) G99731)
(and (setq G99731 20)
(< G99730 G99731)
(setq G99730 G99731)
(setq G99731 30)
(< G99730 G99731)
(setq G99730 G99731)
(setq G99731 (+ 30 3))
(< G99730 G99731)
(setq G99730 G99731)
(setq G99731 (* 10 4))
(< G99730 G99731)
(setq G99730 G99731) t))
This is the idea similar to 6502's, but it may create less code, in a less trivial situation, but it will create more code in a trivial situation.
I have a macro below that iterates along bits in an integer. I would like to integrate the collect capability of the loop like this:
(loop for x in '(a b c d e)
for y in '(1 2 3 4 5)
collect (list x y) )
How should I modify the macro below to accomplish the above?
(defmacro do-bits ((var x) &rest body)
"Evaluates [body] forms after binding [var] to each set bit in [x]"
(let ((k (gensym)))
`(do ((,k ,x (logand ,k (1- ,k))))
((= ,k 0))
(let ((,var (logand ,k (- ,k))))
,#body))))
here's a simple macro with-collector that should do the trick:
(defmacro with-collector ((&optional (collector-name 'collect)) &body body)
(let ((result (gensym)))
`(let ((,result (list)))
(flet ((,collector-name (arg) (push arg ,result)))
(progn ,#body)
(when ,result
(nreverse ,result)))))
it uses the name collect by default:
(with-collector ()
(collect 'a)
(collect 'b)); => (A B)
but you can use another name if you like (e.g. for nesting or resolving a symbol conflict)
(with-collector (foo)
(foo 'bar)
(foo 'baz)); => (BAR BAZ)
to integrate it with your macro, just wrap the do form:
(defmacro do-bits ((var x) &rest body)
"Evaluates [body] forms after binding [var] to each set bit in [x]"
(let ((k (gensym)))
`(with-collector ()
(do ((,k ,x (logand ,k (1- ,k))))
((= ,k 0))
(let ((,var (logand ,k (- ,k))))
,#body)))))
and collect will be available in the body:
(do-bits (x 255) (collect x))
; => (1 2 4 8 16 32 64 128)
(do-bits (x 256) (collect x))
; => (256)
Is there a way to do this:
(defvar long-list ((1 1 1 1) (2 2 2 2) (3 3 3 3)
(4 4 4 4) (5 5 5 5) (6 6 6 6))
(format t "magic" long-list)
To output something like:
(1 1 1 1) (2 2 2 2) (3 3 3 3)
(4 4 4 4) (5 5 5 5) (6 6 6 6)
Where I would define the number of columns to print?
I know about (format t "~/my-function/" long-list) option, but maybe there's something built-in?
The reference is being highly unhelpful on this particular topic.
OK, sorry, I actually found it: http://www.lispworks.com/documentation/lw51/CLHS/Body/f_ppr_fi.htm#pprint-tabular but before I found it, I wrote this:
(defun pplist-as-string (stream fmt colon at)
(declare (ignore colon at))
(dolist (i fmt)
(princ i stream)))
(defun ppcolumns (stream fmt colon at cols)
(declare (ignore at colon))
(when (or (not cols) (< cols 1)) (setq cols 1))
(let* ((fmt-length (length fmt))
(column-height (floor fmt-length cols))
(remainder (mod fmt-length cols))
(printed 0)
columns
column-sizes)
(do ((c fmt (cdr c))
(j 0 (1+ j))
(r (if (zerop remainder) 0 1) (if (zerop remainder) 0 1))
(i 0 (1+ i)))
((null c))
(when (or (= j (+ r column-height)) (zerop i))
(setq columns (cons c columns)
column-sizes
(cons
(+ r column-height) column-sizes))
(unless (zerop remainder)
(unless (zerop i) (decf remainder)))
(setq j 0)))
(setq columns (reverse columns)
column-sizes (reverse column-sizes))
(when (/= fmt-length (* column-height cols))
(incf column-height))
(dotimes (i column-height)
(do ((c columns (cdr c))
(size column-sizes (cdr size)))
((or (null c)))
(when (> printed (1- fmt-length))
(return-from ppcolumns))
(when (< 0 (car size))
(pplist-as-string stream (caar c) nil nil)
(when (caar c) (incf printed))
(unless (null c) (princ #\ ))
(rplaca c (cdar c))))
(princ #\newline))))
which prints it in another direction. In case you would need it.