Reputation: 205
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
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.
Upvotes: 3
Views: 1288
Reputation: 33019
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.
Upvotes: 4