Is it possible to macro returned macro? I would like to simplify the code maximum, and I can do this using a macro that returns the function. However, it is too much overhead and it is too slow. To keep the code more readable, I did not use type hints, but even with them my code is ~ 5x slower.
My English is not very precise, so I write what I have and what I wanted to have.
I have this...
(defmacro series [java-array]
`(fn
([i#]
(aget ~java-array i#))
([start# stop#]
(let [limit# (unchecked-subtract-int stop# start#)
result# (double-array limit#)]
(loop [i# 0]
(if (< i# limit#)
(let [r# (double (aget ~java-array i#))]
(aset result# i# r#)
(recur (inc i#)))
result#))))))
I want something like this...
(defmacro series [java-array]
`(defmacro blabla
([i#]
`(aget ~~java-array i#))
([start# stop#]
`(let [limit# (unchecked-subtract-int stop# start#)
result# (double-array limit#)]
(loop [i# 0]
(if (< i# limit#)
(let [r# (double (aget ~~java-array i#))]
(aset result# i# r#)
(recur (inc i#)))
result#))))))
But when I call this...
Wrong number of args (1) passed to: blabla
A simpler example.
I have a lot of java arrays. I do not want to use aget. I want macro to expand to (aget array-name i). I write a macro that expand to (fn [n] (aget array-name i)), but this is unnecessary overhead.
(defmacro series [arr]
`(fn [i#]
(aget (longs ~arr) (int i#))))
I now declare the series, such as "date", and call it in this way (date i), which will return me "i" element of the array.
I think what you're looking for is a way to declare local macros within functions. The clojure.tools.macro package has a macrolet form, which I think you should be able to wrangle into creating a macro for local array lookups.
Here's a little example I put together:
; project.clj
(defproject testproject "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.5.0"]
[org.clojure/tools.macro "0.1.2"]])
; src/testproject/core.clj
(ns testproject.core
(:require [clojure.tools.macro :refer [macrolet]]))
(defn my-test []
(let [n 1000
arr (int-array n (range 10))]
(macrolet [(lookup [i] (list `aget 'arr i))]
(loop [i 0, acc 0]
(if (< i n)
(recur (inc i) (+ acc (lookup i)))
acc)))))
In the example I use macrolet to declare a macro called lookup that will cause (lookup 5) to expand into (lookup arr 5), which is what I think you're looking for.
Notice how you have to be careful when referencing arr from within the local macro definition. If you just declared the macro as`(aget arr ~i), then it tries to find a fully-qualified symbol arr, which doesn't exist. You could alternatively declare the macro as `(aget ~'arr ~i), but I felt that (list `aget 'arr i) looked a lot nicer.
Now you can take it one step further and use defmacro to create a macro that includes macrolet inside, which you can use to simplify the declaration of arrays with a local lookup macro. We leave this as an exercise for the reader.
Since macros are so subtle, and nesting macros in macros only makes things worse, I decided it would probably be more helpful to just give an example and let you figure out how it works:
(defmacro lookup-binding [[fn-sym value] & body]
(let [array-sym (symbol (str fn-sym "-raw"))]
`(let [~array-sym ~value]
(macrolet [(~fn-sym [i#] (list aget '~array-sym i#))]
~#body))))
(defn my-test2 []
(let [n 1000]
(lookup-binding [arr (int-array n (range 10))]
(loop [i 0, acc 0]
(if (< i n)
(recur (inc i) (+ acc (arr i)))
acc)))))
Disclaimer: I'm only trying to show that this is possible, not that it's a good idea. I personally don't think all this extra complexity is worth avoiding aget. I'd suggest just using aget since it's only a few extra characters, and it will make your code more clear/readable.
Related
I am trying to practise creating macros in Common Lisp by creating a simple += macro and an iterate macro. I have managed to create the += macro easily enough and I am using it within my iterate macro, which I am having a couple of issues with. When I try to run my macro with for example
(iterate i 1 5 1 (print (list 'one i)))
(where i is the control variable, 1 is the start value, 5 is the end value, and 1 is the increment value). I receive SETQ: variable X has no value
(defmacro += (x y)
(list 'setf x '(+ x y)))
(defmacro iterate (control beginExp endExp incExp &rest body)
(let ( (end (gensym)) (inc (gensym)))
`(do ( (,control ,beginExp (+= ,control ,inc)) (,end ,endExp) (,inc ,incExp) )
( (> ,control ,end) T)
,# body
)
)
)
I have tried multiple different things to fix it by messing with the , and this error makes me unsure as to whether the problem is with iterate or +=. From what I can tell += works properly.
Check the += expansion to find the error
You need to check the expansion:
CL-USER 3 > (defmacro += (x y)
(list 'setf x '(+ x y)))
+=
CL-USER 4 > (macroexpand-1 '(+= a 1))
(SETF A (+ X Y))
T
The macro expansion above shows that x and y are used, which is the error.
We need to evaluate them inside the macro function:
CL-USER 5 > (defmacro += (x y)
(list 'setf x (list '+ x y)))
+=
CL-USER 6 > (macroexpand-1 '(+= a 1))
(SETF A (+ A 1))
T
Above looks better. Note btw. that the macro already exists in standard Common Lisp. It is called incf.
Note also that you don't need it, because the side-effect is not needed in your iterate code. We can just use the + function without setting any variable.
Style
You might want to adjust a bit more to Lisp style:
no camelCase -> default reader is case insensitive anyway
speaking variable names -> improves readability
documentation string in the macro/function - improves readability
GENSYM takes an argument string -> improves readability of generated code
no dangling parentheses and no space between parentheses -> makes code more compact
better and automatic indentation -> improves readability
the body is marked with &body and not with &rest -> improves automatic indentation of the macro forms using iterate
do does not need the += macro to update the iteration variable, since do updates the variable itself -> no side-effects needed, we only need to compute the next value
generally writing a good macro takes a bit more time than writing a normal function, because we are programming on the meta-level with code generation and there is more to think about and a few basic pitfalls. So, take your time, reread the code, check the expansions, write some documentation, ...
Applied to your code, it now looks like this:
(defmacro iterate (variable start end step &body body)
"Iterates VARIABLE from START to END by STEP.
For each step the BODY gets executed."
(let ((end-variable (gensym "END"))
(step-variable (gensym "STEP")))
`(do ((,variable ,start (+ ,variable ,step-variable))
(,end-variable ,end)
(,step-variable ,step))
((> ,variable ,end-variable) t)
,#body)))
In Lisp the first part - variable, start, end, step - usually is written in a list. See for example DOTIMES. This makes it for example possible to make step optional and to give it a default value:
(defmacro iterate ((variable start end &optional (step 1)) &body body)
"Iterates VARIABLE from START to END by STEP.
For each step the BODY gets executed."
(let ((end-variable (gensym "END"))
(step-variable (gensym "STEP")))
`(do ((,variable ,start (+ ,variable ,step-variable))
(,end-variable ,end)
(,step-variable ,step))
((> ,variable ,end-variable) t)
,#body)))
Let's see the expansion, formatted for readability. We use the function macroexpand-1, which does the macro expansion only one time - not macro expanding the generated code.
CL-USER 10 > (macroexpand-1 '(iterate (i 1 10 2)
(print i)
(print (* i 2))))
(DO ((I 1 (+ I #:STEP2864))
(#:END2863 10)
(#:STEP2864 2))
((> I #:END2863) T)
(PRINT I)
(PRINT (* I 2)))
T
You can see that the symbols created by gensym are also identifiable by their name.
We can also let Lisp format the generated code, using the function pprint and giving a right margin.
CL-USER 18 > (let ((*print-right-margin* 40))
(pprint
(macroexpand-1
'(iterate (i 1 10 2)
(print i)
(print (* i 2))))))
(DO ((I 1 (+ I #:STEP2905))
(#:END2904 10)
(#:STEP2905 2))
((> I #:END2904) T)
(PRINT I)
(PRINT (* I 2)))
I figured it out. Turns out I had a problem in my += macro and a couple of other places in my iterate macro. This is the final working result. I forgot about the , while i was writing the += macro. The other macro declerations where out of order.
(defmacro += (x y)
`(setf ,x (+ ,x ,y)))
(defmacro iterate2 (control beginExpr endExpr incrExpr &rest bodyExpr)
(let ((incr(gensym))(end(gensym)) )
`(do ((,incr ,incrExpr)(,end ,endExpr)(,control ,beginExpr(+= ,control ,incr)))
((> ,control ,end) T)
,# bodyExpr
)
)
)
Looking at Practical Common Lisp, we're looking at a simple automated unit test framework. We're trying to write a macro to be used as such:
(check (= (+ 1 2) 3) (= (- 1 4) 9))
This should expand to something using a previously defined function report-result. The suggested implementation is:
(defmacro check (&body forms)
`(progn
,#(loop for f in forms collect `(report-result ,f ',f))))
However, that expansion seems rather procedural to me. I wanted to replace the loop with a mapcar to expand to something like this:
(mapcar #'(lambda (form) (report-result form 'form)) (list form-1 ... form-n))
However, I'm clearly lacking the macro-writing skills to do so. Can someone come up with one such macro?
In case it's relevant, this is the definition of report-result:
(defun report-result (result form)
(format t "~:[FAIL~;pass~] ... ~a~%" result form))
It's indeed fairly simple: you just place the collect expression into the body of your mapcar:
(defmacro check (&body forms)
`(progn
,#(mapcar #'(lambda (form)
`(report-result ,form ',form))
forms)))
You don't really need to know anything about the "macro-y" stuff that's going on, in order to do the replacement you want, which is simply replacing a loop with some other equivalent expression: it will work just as well in a macro context as it would outside.
If you want to expand to a mapcar you can, but there's no real reason to do so, since the list's size is known at compile time. Here's what that would look like:
(defmacro check (&body forms)
`(let ((results (list ,#(mapcar #'(lambda (form)
`(list ,form ',form))
forms))))
(mapcar #'(lambda (result)
(report-result (car result) (cadr result)))
results)))
Which expands like so
> (macroexpand-1 '(check (+ 1 2) (* 2 3)))
(let ((results (list (list (+ 1 2) '(+ 1 2))
(list (* 2 3) '(* 2 3)))))
(mapcar #'(lambda (result) (report-result (car result) (cadr result)))
results))
Which as you can see is rather awkward: the macro already has the forms like (+ 1 2) available to it, but in order to preserve them to runtime for the mapcar lambda to see, you have to emit the input form twice. And you have to produce the whole list to map over, rather than just producing a list that's "finished" to begin with. Additionally, this produces a list as output, and requires having all the inputs and outputs in memory at once: the original macro with progn produced the inputs and outputs one at a time, and discarded them when finished.
I created a for loop macro for clojure (based on this answer), which is as follows:
(defmacro for-loop [[sym init check change :as params] & steps]
`(loop [~sym ~init value# nil]
(if ~check
(let [new-value# (do ~#steps)]
(recur ~change new-value#))
value#)))
Now, I want to use this for-loop inside some function, but I can't figure out how to use it inside a function. Could somebody guide me on this, or give me an example with a simple usage of this inside a function?
If you turn it into a function by changing defmacro to defn, then, for example,
(for-loop '[i 0 (< i 10) (inc i)] '(* 2 i))
... produces (stripping the namespace qualifiers)
(loop [i 0 value__11639__auto__ nil]
(if (< i 10)
(let [new-value__11640__auto__ (do (* 2 i))]
(recur (inc i) new-value__11640__auto__))
value__11639__auto__))
Study this to see what it does.
I'm trying to write a macro in Lisp that re-implements let using itself. This is a trivial exercise which has no practical purpose; however after giving a response to a related question, I realized I should probably learn more about macros. They're touted as one of the great things about Lisp, but I rarely use them.
Anyway, here's what I tried first:
(defmacro mylet (args &rest exp) `(let ,args (dolist (x ,exp) x)))
but when I try something like:
(mylet ((a 5) (b 2)) (print (+ a b)))
this throws up an error:
#1=(PRINT (+ A B)) is not a symbol or lambda expression in the form (#1#) .
args (a and b) are set properly, but the print statement doesn't work. I think it's because I'm using two levels of indirection-- referring to a variable that I've created within the macro. But I can't seem to figure out how to fix it! Any ideas?
Your macro expands to:
(LET ((A 5) (B 2))
(DOLIST (X ((PRINT (+ A B)))) X))
which is invalid because ((PRINT (+ A B))) is not a valid expression. There is also an issue that using an interned symbol in macro expansion can lead to variable capture, but that is not directly relevant (read more in PCL).
Using DOLIST here is unnecessary, and compilcated to get right (you would have to convert all subforms to anonymous function in order to stick them in a list, funcall them in sequence and then store the final result in order to conform to PROGN behaviour). You can just use PROGN, or, since LET includes an implicit PROGN, just splice the body using the ,# feature of backquote mechanism:
(defmacro mylet (args &body exp) `(let ,args ,(cons 'progn exp)))
(defmacro mylet (args &body exp) `(let ,args ,#exp))
I'm trying (as a self-learning exercise) to create a Clojure macro that will generate code to apply a function to a sequence of integers and sum the result, e.g.
f(0) + f(1) + f(2) + f(3)
This is my attempt:
(defmacro testsum [func n]
`(fn [x#] (+ ~#( map (fn [i] `(~func x#)) (range n)))))
However something seems to go wrong with the x# gensym and I end up with two different versions of x and hence the function doesn't work:
(macroexpand '(testsum inc 3))
gives:
(fn* ([x__809__auto__]
(clojure.core/+
(inc x__808__auto__)
(inc x__808__auto__)
(inc x__808__auto__))))
This is pretty much exactly what I want apart from the different 809 and 808 versions of x.....
What am I doing wrong? I thought that the auto gensym was meant to create a single unique symbol for exactly this kind of purpose? Is there a better way of doing this?
foo#-style gensyms are valid only inside the syntax-quote where they were created. In your code, the two x#s are created in different syntax-quote blocks:
(defmacro testsum [func n]
`(fn [x#] (+ ~#( map (fn [i] `(~func x#)) (range n)))))
^- s-q1 ^-unquote ^- s-q2
To fix this, use an explicit (gensym) call:
(defmacro testsum [func n]
(let [x (gensym "x")]
`(fn [~x] (+ ~#(map (fn [i] `(~func ~x)) (range n))))))
And the macro expansion ((macroexpand '(testsum inc 3))):
(fn* ([x4966] (clojure.core/+ (inc x4966) (inc x4966) (inc x4966))))