是否可以宏返回宏?我想简化代码最大化,我可以使用返回函数的宏来完成此操作。但是,它的开销太大而且速度太慢。为了使代码更具可读性,我没有使用类型提示,但即使使用它们,我的代码也慢了约5倍。
我的英语不是很精确,所以我写下我拥有的和我想要的东西。
我有这个......
(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#))))))
我想要这样的东西......
(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#))))))
但是当我称之为......
Wrong number of args (1) passed to: blabla
我有很多java数组。我不想用aget。我希望宏扩展到(aget array-name i)
。我写了一个扩展到(fn [n] (aget array-name i))
的宏,但这是不必要的开销。
(defmacro series [arr]
`(fn [i#]
(aget (longs ~arr) (int i#))))
我现在声明系列,例如“date”,并以这种方式调用它(date i)
,它将返回数组的“i”元素。
答案 0 :(得分:4)
我认为您正在寻找的是一种在函数中声明本地宏的方法。 clojure.tools.macro
包具有macrolet
形式,我认为您应该能够为本地数组查找创建宏。
这是我放在一起的一个小例子:
; 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)))))
在示例中,我使用macrolet
来声明一个名为lookup
的宏,它会导致(lookup 5)
扩展为(lookup arr 5)
,这就是我认为你的意思寻找。
请注意在本地宏定义中引用arr
时必须小心。如果您刚刚将宏声明为`(aget arr ~i)
,那么它会尝试查找一个完全限定的符号arr
,该符号不存在。您也可以将宏声明为`(aget ~'arr ~i)
,但我觉得(list `aget 'arr i)
看起来好多了。
现在您可以更进一步,使用defmacro
创建一个内部包含macrolet
的宏,您可以使用它来简化使用本地查找宏的数组声明。 我们将此作为练习留给读者。
由于宏是如此微妙,并且在宏中嵌套宏只会使事情变得更糟,我决定只举一个例子并让你弄清楚它是如何工作的可能会更有帮助:
(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)))))
免责声明: 我只是想表明这是可能的,而不是它是一个好主意。我个人认为所有这些额外的复杂性都值得避免aget
。我建议只使用aget
,因为它只需要几个额外的字符,它会使您的代码更清晰/可读。