Clojure宏以常量收集字符串

时间:2010-02-25 23:05:03

标签: macros clojure

我有一个包含很多字符串常量的Clojure文件。我想通过将它们包装在宏中来收集集合中的这些字符串。经过几次尝试,我成功了,但我的解决方案看起来相当可怕。

(ns Memorable)
(def MEMORY (atom []))
(defmacro memorize [s] (swap! MEMORY conj s) s)

(prn (str (memorize "hello") " brave new " (memorize "world")))  ;  test

(defmacro make-memories-constant [] `(def MEMORIES ~(deref MEMORY)))
(make-memories-constant)

这个问题有更优雅的解决方案吗?

1 个答案:

答案 0 :(得分:1)

就代码清理而言,我会删除make-memories-constant宏 - 你可以做到

(def MEMORIES @MEMORY)

甚至

(def MEMORY @MEMORY)

以免混淆你的命名空间与另一个Var。只要您在调用memorize之后放置此内容,就会保存MEMORY的快照,其中包含所有memorize个字符串。

我认为这实际上非常干净,实际执行的代码与没有任何memorize时的实际代码没有区别......

另一种方法是准备一种“记忆框架”作为一个单独的命名空间,导出一个名为setup-memorization的宏,比如说,它可能看起来像这样(只是一个粗略的草图如果没有一些抛光 ... 更新(不太彻底)测试版本 - 这实际上有效! ...仍然,请调整它你的需求):

(ns memorization-framework)

(defmacro setup-memorization []
  (let [MEMORY (gensym "MEMORY")
        MEMORIES (gensym "MEMORIES")]
    `(do (def ~MEMORY (atom []))
         (defmacro ~'memorize [s#] (swap! ~MEMORY conj s#) s#)
         (defmacro ~'get-memory [] @~MEMORY)
         (defmacro ~'defmemories [~MEMORIES]
           `(do (def ~~MEMORIES @~~MEMORY)
                (ns-unmap ~~*ns* '~'~'get-memory)
                (ns-unmap ~~*ns* '~'~'memorize)
                (ns-unmap ~~*ns* '~'~MEMORY)
                ~~MEMORIES)))))

然后你use memorization-framework命名空间,在命名空间顶部做(setup-memorization)进行设置,像在示例代码中一样调用memorize记住事物并最终使用 end-memorization defmemories将字符串集合存储在某处并删除用于存储所用原子的临时Var用于构造时存储以及从此命名空间进一步调用memorize的功能。 ( end-memorization宏只是在没有参数的情况下将集合返回到您调用它的任何地方,或者如果给定一个参数,则定义一个新的Var来存储它,该参数必须是一个符号用来命名Var。更新:我只测试了defmemories版本,所以我将它留在这里并删除“就地返回”变体。)

在命名空间foo中REPL的示例交互(注意我在setup-memorization命名空间中定义了user):

foo> (user/setup-memorization)
#'foo/defmemories
foo> (get-memory)
[]
foo> (memorize "foo")
"foo"
foo> (memorize "bar")
"bar"
foo> (get-memory)
["foo" "bar"]
foo> (defmemories quux)
["foo" "bar"]
foo> quux
["foo" "bar"]
foo> (get-memory)
; Evaluation aborted.
foo> (memorize)
; Evaluation aborted.

那些“评价中止了”。消息表示在调用get-memory后调用memorizedefmemories会抛出异常。如上所述,这是设计的。此外,get-memory主要是为了简化测试/调试。

如果您只打算使用所有这一次,那么setup-memorization方法可能会有点过分,但我想如果您再使用它,它会为您删除一些样板。