我有一个包含很多字符串常量的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)
这个问题有更优雅的解决方案吗?
答案 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
后调用memorize
和defmemories
会抛出异常。如上所述,这是设计的。此外,get-memory
主要是为了简化测试/调试。
如果您只打算使用所有这一次,那么setup-memorization
方法可能会有点过分,但我想如果您再使用它,它会为您删除一些样板。