【问题标题】:unquote [& args] on macro在宏上取消引用 [& args]
【发布时间】:2014-03-13 08:02:57
【问题描述】:

如果我想定义一个接受未知数量参数的函数,我可以轻松地执行以下操作:

(defn foo
  [& args]
  args) 

它返回一个clojure.lang.ArraySeq 类型的值,其中包含函数中给定的值。

在宏中,使用~@ 是取消引用拼接,这会使给定的参数变平:

(defmacro foo
  [bar & baz]
  `(println ~@baz))

如果我像这样使用宏:

(foo "foo" 1 2 3 4)

它将打印出1 2 3 4。但是,我想让 args 不平坦。做~baz(没有@)给了我一个例外。知道怎么做吗?

【问题讨论】:

    标签: macros clojure


    【解决方案1】:

    如果你macroexpand-1的版本使用~baz,问题的原因就很明显了:

    (macroexpand-1 '(foo "foo" 1 2 3 4)) ; NB. using ~baz in foo's body
    ;= (println (1 2 3 4))
    ;            ^
    

    所以我们在这里尝试将1 作为一个函数来调用。 (注意,此调用发生并在运行时引发异常。)

    解决此问题的正确方法取决于您想要实现的目标。例如,您可以将其余的 arg 倒入向量中并打印:

    (defmacro foo [bar & baz]
      `(println ~(vec baz)))
    

    【讨论】:

    • 感谢macroexpand-1
    【解决方案2】:

    对 Michał Marczyk 的回答的评论...

    您还可以通过将宏转换为函数来查看发生了什么:

    (defn foo [bar & baz]
      `(println ~@baz))
    
    => (foo "foo" 1 2 3 4)
    (clojure.core/println (1 2 3 4))
    

    语法引用、取消引用和取消引用拼接仍然有效。啊哈!宏与函数的不同之处在于它们的调用方式,而不是它们的作用。

    【讨论】:

      【解决方案3】:

      你总是可以做到的

      (defmacro foo
        [bar & baz]
        `(println (list ~@baz)))
      

      构造一个seq或

      (defmacro foo
        [bar & baz]
        `(println [~@baz]))
      

      构造一个向量。

      【讨论】:

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