【问题标题】:Clojure macro to collect strings in constantClojure 宏以常量收集字符串
【发布时间】:2010-02-25 23:05:03
【问题描述】:

我有一个包含大量字符串常量的 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)

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

【问题讨论】:

  • 为什么memorize 是一个宏?为什么要将identity 映射到一个向量,然后创建一个新的结果向量?
  • @Mike Douglas:将memorize 设为宏可以消除运行时的函数调用开销(当宏展开时,conj 会发生在MEMORY 上)。不过同意第二点。
  • 这样做的目的是什么?是否要收集字符串用于翻译目的?
  • 地图是不必要的,我现在已经删除了。宏是必需的,不是为了性能,而是因为在编译代码时,会跳过宏扩展阶段,MEMORY 将为空。
  • @Stuart 我有一个带有法语语法命名规则的 clojure 文件。一个例子是:在以“[aou]”开头的结尾之前将“c”变成“ç”。我需要一个规则列表,给定法语句子的代码,可以判断应用了哪些规则,以及给定命名规则可以使用该规则生成法语句子的代码(例如“Nous commençons”)。我希望在一个地方定义每个规则的所有数据。

标签: macros clojure


【解决方案1】:

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

(def MEMORIES @MEMORY)

甚至

(def MEMORY @MEMORY)

以免你的命名空间与另一个 Var 混淆。只要您在所有调用 memorize 后将其放入,这将保存 MEMORY 的快照,其中包含所有 memorized 字符串。

我想说这实际上很干净,在运行时执行的实际代码与没有任何 memorize-ing 的情况没有什么不同...

另一种方法是准备一种“记忆框架”作为单独的命名空间,导出一个名为 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 将字符串集合 somewhere 存储在 Var 中,并删除用于存储用于构建时存储的原子的临时 Var从此命名空间调用memorize。 (end-memorization 宏意味着在不带参数调用时将集合返回到您调用它的任何位置,或者如果给定单个参数,则定义一个新的 Var 来存储它,该参数必须是用于命名 Var。 更新:我只测试了 defmemories 版本,所以我将它留在这里并删除“就地返回”变体。)

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

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-memorymemorize 的调用在调用defmemories 后会引发异常。如上所述,这是设计使然。另外,get-memory 主要是为了方便测试/调试。

如果您只打算使用这一切,setup-memorization 方法可能有点矫枉过正,但我​​想如果您更多地使用它,它会为您删除一些样板。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-03
    • 1970-01-01
    • 1970-01-01
    • 2015-09-17
    • 1970-01-01
    • 2017-04-17
    • 1970-01-01
    相关资源
    最近更新 更多