【问题标题】:Clojure's macro - define a binding whose name is composed from an argumentClojure 的宏 - 定义一个名称由参数组成的绑定
【发布时间】:2011-05-16 08:37:24
【问题描述】:

好的,我想写一个 Clojure 宏来定义一个 struct-map 并让调用者为每个字段指定类型。

签名将如下所示:

(defmodel category :id Integer :name String)

它的作用是创建一个名为struct-map的类别,并创建一个绑定*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* ...) 不起作用。

我怎样才能做到这一点?

谢谢。

【问题讨论】:

    标签: macros clojure metaprogramming


    【解决方案1】:

    (使用问题文本中的宏的调试版本进行了更新。)

    这应该按规定工作:

    (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,但主要缺点是您必须在初始化时提供所有字段值。例如,(defrecord category [^String id ^String name]),我必须这样称呼它(category. 1 "stack")。我不能做类似 (category. {:id 1 :name "Stack"}) 或 (category. 1) 之类的事情,即允许默认参数。似乎记录支持仍然是暂定的,并且可能随时更改。随意插话,告诉我到目前为止我不知道的关于记录的任何事情:)
    • 我会考虑写一个承包商来做你想做的事。记录速度更快,并且可以实现协议。一些更强大的记录可以在这里找到github.com/david-mcneil/defrecord2
    • @EnToutCas:我不会暂定记录支持——结构映射确实已经被弃用了,建议替换记录。 (这并没有说明工厂函数的某些约定将来可能会进入核心库的可能性。)至于您的具体问题,我基本上同意nickik。 (事实上​​,我发现为我几乎所有的记录编写工厂函数很有用,如果以后能够在没有:import-ing 所有记录的情况下:require 他们。)@nickik:感谢链接!跨度>
    • @EnToutCas:哦,还有一件事。在 Clojure 中,使用普通的 ol' 映射而不是结构映射或记录通常是非常有意义的。你考虑过吗? (加上自定义函数完成assoc & Co. 的工作,同时对您的*foo-meta* Vars 执行数据类型检查,也许。)
    • @Michal,我认为结构映射是带有预定义键的映射,不是吗?
    猜你喜欢
    • 2015-08-05
    • 2012-10-07
    • 2019-02-15
    • 1970-01-01
    • 2022-12-18
    • 2016-06-26
    • 2012-10-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多