Macros Clojure';s宏-定义名称由参数组成的绑定
好的,我想编写一个Clojure宏,它定义一个结构映射,并让调用方为每个字段指定类型 签名将如下所示:Macros Clojure';s宏-定义名称由参数组成的绑定,macros,clojure,metaprogramming,Macros,Clojure,Metaprogramming,好的,我想编写一个Clojure宏,它定义一个结构映射,并让调用方为每个字段指定类型 签名将如下所示: (defmodel category :id Integer :name String) 它的作用是创建一个名为category的结构映射,并创建一个绑定*category meta*,它是一个映射{:id Integer:name String} 以下是我实现这一目标的宏: (defmacro defmodel [name & field-spec] `(let [fiel
(defmodel category :id Integer :name String)
它的作用是创建一个名为category的结构映射
,并创建一个绑定*category meta*
,它是一个映射{:id Integer:name String}
以下是我实现这一目标的宏:
(defmacro defmodel [name & field-spec]
`(let [fields# (take-nth 2 ~@field-spec)]
(defstruct ~name fields#)
(def *~name-meta* (reduce #(assoc %1 (first %2) (last %2))) (partition 2 ~@field-spec))))
但是,问题是,我无法定义一个名称由另一个名称组成的绑定。基本上,(def*~name meta*..)
不起作用
我怎样才能做到这一点
谢谢。(使用问题文本中的宏调试版本进行更新。)
这应按规定进行:
(defmacro defmodel [name & field-spec]
`(do (defstruct ~name ~@(take-nth 2 field-spec))
(def ~(symbol (str "*" name "-meta*"))
(reduce #(assoc %1 (first %2) (last %2))
{}
(partition 2 '~field-spec)))))
主要问题的答案是使用~(symbol(str“*”name“-meta*”)
代替*~name meta*
~
以语法引号形式取消下一个表达式的引号,将其返回值注入给定列表结构的适当位置
其他一些修改是必要的——特别是,defstruct
要求将键作为单独的参数提供给它,而不是一个seq(或包含这样一个seq的变量的名称),reduce
需要显式的种子值在这里工作,等等
顺便说一句,除非您需要坚持Clojure 1.1,否则您可能希望使用1.2的
defrecord
,而不是defstruct
——事实上,后者在1.2中已被弃用。感谢您的明确回答。我以前看过defrecord,但主要缺点是必须在初始化时提供所有字段值。e、 例如,(defrecord category[^String id^String name]),我必须像这样调用它(category.1“stack”)
。我不能执行(category.{:id 1:name“Stack”})或(category.1)之类的操作,即允许使用默认参数。似乎记录支持仍然是暂时的,可能随时改变。请随时插手告诉我关于我到目前为止还没有的任何记录:)我会考虑写一个承包商来做你想做的事情。记录速度更快,并且可以实现协议。在@entootcas中可以找到一些更强大的记录:我不认为记录支持是暂时的——毕竟结构映射确实已经被弃用了,建议使用记录作为替代。(这并没有说明工厂函数的某些约定将来可能会进入核心库。)至于您的具体问题,我基本上同意nickik的观点。(事实上,我发现为我的几乎所有记录编写工厂函数非常有用,只要能够:以后需要:导入-屏蔽所有记录即可。)@nickik:感谢链接@恩图卡斯:哦,还有一件事。在Clojure中,与结构映射或记录相比,使用普通ol映射通常是非常有意义的。你考虑过吗?(加上执行assoc
&Co.工作的自定义函数,可能在对*foo meta*
变量执行数据类型检查时。)@Michal,我认为结构映射是带有预定义键的映射,不是吗?