我正在尝试编写一个将生成n个函数的宏.这是我到目前为止所拥有的:
; only defined this because if I inline this into make-placeholders
; it's unable to expand i# in ~(symbol (str "_" i#))
(defmacro defn-from [str mdata args & body]
`(defn ~(symbol str) ~mdata ~args ~@body))
; use list comprehension to generate n functions
(defmacro make-placeholders [n]
`(for [i# (range 0 ~n)] (defn-from (str "_" i#) {:placeholder true} [& args] (nth args i#))))
; expand functions _0 ... _9
(make-placeholders 9)
Run Code Online (Sandbox Code Playgroud)
我得到的错误是:
java.lang.ClassCastException: clojure.lang.Cons cannot be cast to java.lang.String
Run Code Online (Sandbox Code Playgroud)
而且我不确定这意味着什么,但我有这个模糊的概念(因为...)没有像我认为的那样在宏内部工作.
ama*_*loy 17
您对运行时和编译时之间以及宏和函数之间的区别感到困惑.与解决宏观问题eval从来都不是1正确答案:不是,请确保您返回代码,不会要发生什么.这是使您的原始版本有效的最小变化.
主要变化是:
defn-from是一个函数,而不是一个宏 - 你只是想要一种方便的方法来创建列表,主宏负责插入结果表单.你不希望宏在这里,因为你不希望它扩展到身体做make-placeholders.
make-placeholders与开始do的,使其for 外一个语法引号.这是最重要的部分:你希望返回给用户的代码看起来像(do (defn ...)),就像他们手动输入所有代码一样- 不是 (for ...),它只能定义一个函数.
(defn defn-from [str mdata args & body]
`(defn ~(symbol str) ~mdata ~args ~@body))
; use list comprehension to generate n functions
(defmacro make-placeholders [n]
(cons `do
(for [i (range 0 n)]
(defn-from (str "_" i) {:placeholder true}
'[& args]
`(nth ~'args ~i)))))
user> (macroexpand-1 '(make-placeholders 3))
(do (clojure.core/defn _0 {:placeholder true} [& args] (clojure.core/nth args 0))
(clojure.core/defn _1 {:placeholder true} [& args] (clojure.core/nth args 1))
(clojure.core/defn _2 {:placeholder true} [& args] (clojure.core/nth args 2)))
Run Code Online (Sandbox Code Playgroud)
1非常非常罕见
您也可以在没有宏的情况下完全执行此操作,方法是使用函数创建函数并使用较低级别的操作intern代替def.事实证明它更简单了:
(letfn [(placeholder [n]
(fn [& args]
(nth args n)))]
(doseq [i (range 5)]
(intern *ns* (symbol (str "_" i))
(placeholder i))))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4713 次 |
| 最近记录: |