【问题标题】:unquote a list in Clojure macro在 Clojure 宏中取消引用列表
【发布时间】:2015-04-11 13:32:17
【问题描述】:

我尝试定义一个宏来从一种特殊形式的文本中提取信息,[0 => "0\n"],实际上是 Clojure 中的一个列表。

现在假设我只想使用宏 get-input 获取其中的 first 部分。

(println (get-input [0 => "0\n"])) ; i.e. 0

下面这个效果很好。

; this works
(defmacro get-input
  [expr]
  (let [input (first expr)]
    input))

但是当我使用 Syntax-Quote,即反引号时,事情变得混乱了。 只需将expr~ 取消引用就可以了。 虽然实际上我从未使用过exprsecond 部分,即=>,但似乎它仍然在后面被评估。

CompilerException java.lang.RuntimeException: Unable to resolve symbol: => in this context

; this sucks
(defmacro get-input
  [expr]
  `(let [input# (first ~expr)]
     input#))

我想知道第一个解决方案和第二个解决方案有什么区别。

【问题讨论】:

    标签: clojure macros quote


    【解决方案1】:

    宏在编译时被扩展,宏的主体被评估。如果它是语法引用的,则仅计算未引用的表达式,但如果没有引用(如在您的第一个宏定义中),则在编译时计算主体。

    如果你用你的第一个定义(没有引号)扩展(get-input [0 => "0\n"]),你将拥有

    > (macroexpand '(get-input [0 => "0\n"]))
    0
    

    0 是宏扩展的结果,这意味着所有对(get-input [0 => "0\n"]) 的调用将在编译时被0 替换。

    使用第二个定义,扩展将类似于(生成的符号会不同)

    > (macroexpand '(get-input [0 => "0\n"]))
    (let* [input__11966__auto__ (first [0 => "0\n"])] 
      input__11966__auto__)
    

    展开后,Clojure 编译器将评估向量[0 => "0\n"],并如clojure doc 所述状态:

    向量、集合和映射产生向量和(散列)集合和映射,其内容是它们包含的对象的评估值

    向量的每个元素都按顺序求值,这对0 很好(求值为数字0),但不适用于未知符号=>

    您可能正在寻找的是扩展表达式(在您的情况下是向量),而不评估其内容。为此,您需要quote ~ 的结果,例如

    (defmacro get-input-as-str
      [expr]
      `(let [a# (quote ~expr)]
         (map str a#)))
    

    其中扩展为 - 注意 '[...]:

    > (macroexpand '(get-input-as-str [0 => "0\n"]))
    (let* [a__12040__auto__ '[0 => "0\n"]] (map str a__12040__auto__))
    

    并给出

    > (get-input-as-str [0 => "0\n"])
    ("0" "=>" "0\n")
    

    【讨论】:

    • 很棒的解释!对了,(quote ~expr)可以简化为'~expr
    • Tkx,是的,你是对的,只是为了让它更明确。
    【解决方案2】:

    您收到此异常是因为您尝试取消引用 [0 => "0\n"] 表单,这不是有效的 Clojure 代码。您可以通过更改 first 的顺序和取消引用操作来修复它:

    (defmacro get-input
      [expr]
      `(let [input# ~(first expr)]
         input#))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多