clojure宏来生成函数 [英] clojure macro to generate functions
问题描述
我想写一个将生成n个函数的宏。这是我到目前为止:
;只定义这个,因为如果我内联这到make-placeholder
;它不能扩展i#in〜(symbol(str_i#))
(defmacro defn-from [str mdata args& body]
`(defn〜(symbol str)〜mdata 〜args〜@ body))
;使用列表推导生成n个函数
(defmacro make-placeholders [n]
`(for [i#(range 0〜n)](defn-from(str_i#){ placeholder true} [& args](nth args i#))))
;扩展函数_0 ... _9
(make-placeholders 9)
是:
java.lang.ClassCastException:clojure.lang.Cons不能转换为java.lang.String
我不确定这是什么意思,但我有这个模糊的概念
你对运行时和编译之间的区别感到困惑,时间,宏和函数之间。使用 eval
解决宏问题从来不是 1 正确的答案,而是确保返回的代码能够实现你想要的结果。
主要的更改是:
-
defn-from
是一个函数,而不是一个宏 - 你只是想方便地创建列表,主宏负责插入结果形式。您不想在这里想要一个宏,因为您不想将它扩展到make-placeholders
的正文中。
-
make-placeholder
以do
开头, code> for outside 。这是最重要的部分:你希望返回给用户的代码看起来像(do(defn ...))
,就好像他们键入所有
(defn defn-from [str mdata args& body]
`(defn〜(symbol str)〜mdata〜args〜@ body))
;使用列表推导生成n个函数
(defmacro make-placeholders [n]
(cons'do
(for [i(range 0 n)]
(defn-from str_i){:placeholder true}
'[& args]
`(nth〜'args〜i)))))
user> ($)
(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] core / nth args 2)))
1 / p>
编辑
通过使用函数创建函数并使用低级操作 intern
而不是 def
。事实证明是更简单,事实上:
(letfn [(placeholder [n]
(fn [ & args]
(nth args n))]
(doseq [i(range 5)]
(intern * ns * $ b(placeholder i))))
I'm trying to write a macro that will generate n functions. Here's what I have so far:
; 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)
The error I get is:
java.lang.ClassCastException: clojure.lang.Cons cannot be cast to java.lang.String
And I'm not really sure what that means, but I have this vague notion that (for ...) isn't working the way I think it is inside a macro.
You are getting confused about the distinction between runtime and compile-time, and between macros and functions. Solving a macro problem with eval
is never1 the right answer: instead, make sure that you return code that does what you want to happen. Here's a minimal change to make your original version work.
The main changes are:
defn-from
is a function, not a macro - you just want a convenient way to create lists, which the main macro is responsible for inserting into the result form. You do not want a macro here, because you don't want it expanded into the body ofmake-placeholders
.make-placeholders
starts with ado
, and does itsfor
outside of a syntax-quote. This is the most important part: you want the code returned to the user to look like(do (defn ...))
, as if they'd typed it all in by hand - not(for ...)
, which could only ever def a single function.
(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)))
1 Very, very rarely
Edit
You can also do this completely without macros, by using a function to create functions and using the lower-level operation intern
instead of def
. It turns out to be much simpler, in fact:
(letfn [(placeholder [n]
(fn [& args]
(nth args n)))]
(doseq [i (range 5)]
(intern *ns* (symbol (str "_" i))
(placeholder i))))
这篇关于clojure宏来生成函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!