Clojure 宏 : perform action only if a symbol is defined

标签 clojure macros

我有一个宏 defprinter,我可以在其中定义函数:根据解构获取 dic 中的值 + 打印该变量。

这看起来像:

(defmacro defprinter [name pattern] 
  (list 'defn name [pattern] '(prn to-print)))

所以我可以做类似(defprinter print-lemons {to-print :lemons}) 然后(print-lemons {:lemons "lemons"})它将打印正确的东西(我可以在第一个参数上定义具有任何类型解构的打印机)。

但现在我想提供该功能的选项,也许还知道如何使用颜色打印,例如,如果定义了符号 color,它应该执行 (prn color "-colored "to-print),否则只是(prn color)

因此,(defprinter print-lemons {to-print :lemons}) 生成的函数应该和上面的一样工作,而 (defprinter print-lemons {to-print : lemons, color :color}) 将编写一个执行彩色版本的函数。

此外,如果我这样做,我想要同样的效果 (let [color "black"] (defprinter print-lemons {to-print :lemons}))(def color "black") (defprinter print-lemons {to-print :lemons})柠檬}).

这可能吗?

我试过这样写:

(defmacro defprinter [name pattern] 
  (list 'defn name [pattern]
   '(try (eval '(prn (str color "-colored " to-print))) 
         (catch java.lang.RuntimeException _ 
           (prn to-print)))))

根据我的理解,宏将编写的函数将尝试在运行时评估表达式,如果未定义 color 则失败并抛出 RuntimeException,然后执行 (prn to-print)。即使它会在运行时检查 color 是否存在,to-print(对于该函数始终需要存在)也会在编译时检查宏展开。

但是这里发生的是,我总是得到一个 RuntimeException,即使定义了 color(即使我只在 eval 语句上留下 to-print,它可以'找到它,但 catch 中的子句工作正常)。似乎符号没有像我在评估期间所期望的那样被解析,但我想不出任何其他方法来实现这一点。

有什么建议吗?

最佳答案

首先,将您在这里处理的两个问题分开可能是有意义的:定义一个函数,以及根据可用的变量/局部变量确定如何打印。使宏尽可能简单往往更容易弄清楚发生了什么。

在决定打印什么时,您确实有 3 种情况:

  1. color是本地的
  2. color 是一个 var(我猜是类?)
  3. color 未定义

&env 是一个有用且很少使用的工具(仅在宏中可用),可让您查看本地可用的内容。

resolve 让您可以查看具有该名称的变量。

所以在这个例子中,有 3 个潜在的表达式可以构成用 def-color-printer 定义的函数的主体:

(defn make-print-expression [env]
  (if (contains? env 'color)
    `(prn (str ~'color "-colored " ~'to-print))
    (if-let [color (resolve 'color)]
      `(prn (str @~color "-colored " ~'to-print))
      `(prn ~'to-print))))

(defmacro def-color-printer [name pattern]
  (list `defn name [pattern]
        (make-print-expression &env)))

(def-color-printer print-limes {to-print :limes})

(let [color "green"]
  (def-color-printer print-limes-color-with-local {to-print :limes}))

(def color "greyish")
(def-color-printer print-limes-color-with-var {to-print :limes})

(print-limes {:limes "limes!"})
;=> "limes!"

(print-limes-color-with-local {:limes "limes!"})
;=> "green-colored limes!"

(print-limes-color-with-var {:limes "limes!"})
;=> "greyish-colored limes!"

我还写了a blog about Clojure's quoting以防语法引用语法不熟悉。

关于Clojure 宏 : perform action only if a symbol is defined,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44315467/

相关文章:

c - 宏观守卫

c - 仅在 Visual 中出现奇怪的语法错误 C2143(在 ';' 之前缺少 'type')

macros - 如何从常量值和值集合生成 `quote::Tokens`?

运行 Leiningen 时出现 java.lang.NoSuchMethodError : clojure. lang.KeywordLookupSite

scala - 为什么这个 Clojure 程序处理可变数组的速度如此之慢?

haskell - 有什么证据表明 Clojure Zippers 会因表达为 comonad 而受益?

c - 这个 C 代码块如何解析为整数赋值?

clojure - Clojure 中的可变参数函数错误

Clojure deftype 调用函数在同一个命名空间中抛出 "java.lang.IllegalStateException: Attempting to call unbound fn:"

用于宏粘贴的 C++ 替代方案