【问题标题】:Mapping a symbol-binding macro (in a fn) in Clojure在 Clojure 中映射符号绑定宏(在 fn 中)
【发布时间】:2016-12-27 22:16:54
【问题描述】:

我定义了一个宏来将一个从字符串派生的符号绑定到这样的字符串:

lein repl
... Clojure 1.8.0 ...
user=> (defmacro foo [s] `(def ~(symbol s) ~s))
#'user/foo

在顶层调用时按预期工作:

user=> (foo "asdf")
#'user/asdf
user=> asdf
"asdf"

但是当我尝试映射一个通过序列调用宏的函数时,宏会绑定函数参数符号而不是我想要的:

user=> (map (fn [x] (foo x)) ["qwer"])
(#'user/x)
user=> x
"qwer"
user=> qwer
CompilerException ... Unable to resolve symbol: qwer ...

以下替代方案绑定 Clojure 创建的临时符号:

user=> (map #(foo %) ["qwer"])
(#'user/p1__1253#)

正如我在 StackOverflow 上研究的一些现有答案所建议的那样,当包裹在 doall 中时它也不起作用。

如何定义一个可以映射(在函数中或以其他方式)到字符串集合的符号绑定宏?

【问题讨论】:

    标签: clojure macros


    【解决方案1】:

    map 是一个函数,foo 是一个宏。由于宏扩展发生在编译时并且函数在运行时执行,因此定义一个可以映射(并因此在运行时扩展)的符号绑定宏是不可能的。

    你可以这样做:

    (defn foo2 [s]
      `(def ~(symbol s) ~s))
    
    (defmacro foos [ss]
      `(do ~@(map foo2 ss)))
    
    (foos ["asdf" "qwer"])
    
    asdf ;; => "asdf"
    qwer ;; => "qwer"
    

    现在反过来了:使用函数 mapfoo 扩展宏。

    【讨论】:

      【解决方案2】:

      这是一种方法。该解决方案首先展示了 foo 的工作原理,然后使用具有函数 @987654323 的中间解决方案@ 然后是eval

      最终解决方案使用第二个 map-foo-mcr。这似乎是必要的,因为(def ...) 是一种特殊形式。这类似于(但不完全相同)“海龟一路向下”的问题,在一个地方使用宏要求所有调用者也是宏,而不是函数。

      (ns clj.core
        (:require
          [tupelo.core      :as t]  ))
      (t/refer-tupelo)
      
      (defmacro foo 
        [arg]
        `(def ~(symbol arg) ~arg))
      
      (foo "aa")
      (spyx aa)
      
      (defn map-foo-fn
        [coll]
        (cons 'do
          (forv [elem coll]
            (list 'foo elem))))
      (newline)
      (prn  (map-foo-fn ["bb"] ))
      (eval (map-foo-fn ["bb"] ))
      (spyx bb)
      
      (defmacro map-foo-mcr
        [coll]
        `(do
           ~@(forv [elem coll]
              (list 'foo elem))))
      (newline)
      (println (macroexpand-1 '(map-foo-mcr ["cc" "dd"] )))
      (map-foo-mcr ["cc" "dd"] )
      (spyx cc)
      (spyx dd)
      

      结果:

      aa => "aa"
      
      (do (foo "bb"))
      bb => "bb"
      
      (do (foo cc) (foo dd))
      cc => "cc"
      dd => "dd"
      

      请记住,虽然宏可以做一件函数不能做的事情(避免 arg 评估),但宏不能 做其他功能可以做的事情。特别是,宏不能传递给需要高阶函数参数的mapet al

      欲了解更多详情,请参阅http://www.braveclojure.com/writing-macros 并搜索“Macros All the Way Down”

      注意project.clj需要

      :dependencies [
          [tupelo "0.9.13"]
      

      spyx工作

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-07
        • 1970-01-01
        • 1970-01-01
        • 2011-02-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多