【问题标题】:How to bind var's name and value in the clojure macro?如何在clojure宏中绑定var的名称和值?
【发布时间】:2019-02-15 15:56:27
【问题描述】:

假设我有一些(超过 20 个)变量,我想将它们保存到文件中。我不想重复 20 次相同的代码。 我写了一个宏,但它给了我一个错误。

我的测试用例:

;-----------------------------------------------
(defn processor [ some-parameters ]

  (let [
  ;after some operation ,got these data:
        date-str ["JN01","JN02","JN03","JN04"];length 8760
        date-temperature (map #(str %2 "," %1) [3.3,4.4,5.5,6.6] date-str) ; all vector's length are 8760
        date-ws (map #(str %2 "," %1) [0.2,0.1,0.3,0.4] date-str)          ; 
        ;... many variables such like date-relative-humidity,date-pressure, name starts with "date-",
        ; all same size
        ]
    ;(doseq [e date-temperature]
    ;  (println e))
    (spit "output-variable_a.TXT"
          (with-out-str
            (doseq [e date-temperature]
              (println e))))
    ;same 'spit' part will repeat many times
    ))

(processor 123)
; I NEED to output other variables(ws, wd, relative-humidity, ...)
; Output example:
;JN01,3.3
;JN02,4.4
;JN03,5.5
;JN04,6.6
;-----------------------------------------------

我想要的是一个可以这样使用的宏/函数:

(write-to-text temperature,ws,wd,pressure,theta-in-k,mixradio)

这个宏/函数将完成这项工作。 我不知道怎么写这样的宏/函数。

我的宏在这里发帖,但它不起作用:

(defmacro write-array [& rest-variables ]
  `(doseq [ vname# '~rest-variables ]
     ;(println vname# vvalue#)
     (println "the vname# is" (symbol vname#))
     (println "resolve:" (resolve (symbol (str vname# "-lines"))))
     (println "resolve2:" (resolve (symbol (str "ws-lines"))))
     (let [ vvalue# 5] ;(var-get (resolve (symbol vname#)))]
       ;----------NOTE:  commented out cause '(symbol vname#)' won't work.
       ;1(spit (str "OUT-" vname# ".TXT" )
       ;1      (with-out-str
       ;1        (doseq [ l (var-get (resolve (symbol (str vname# "-lines"))))]
       ;1          (println l))))

       (println vname# vvalue#))))

我发现问题出在(symbol vname#) 部分,此方法仅适用于 GLOBAL 变量,不能绑定到日期温度in LET form,(symbol vname#) 返回 nil。

【问题讨论】:

    标签: variables clojure macros lisp


    【解决方案1】:

    您似乎想使用绑定名称及其值从let 中编写一个分隔值文件。宏在编译期间会转换代码,因此它们无法知道您传递的符号所绑定到的运行时 。您可以使用宏来发出将在运行时评估的代码:

    (defmacro to-rows [& args]
      (let [names (mapv name args)]
        `(cons ~names (map vector ~@args))))    
    
    (defn get-stuff []
      (let [nums [1 2 3]
            chars [\a \b \c]
            bools [true false nil]]
        (to-rows nums chars bools)))
    
    (get-stuff)
    => (["nums" "chars" "bools"]
        [1 \a true]
        [2 \b false]
        [3 \c nil])
    

    或者,您可以为每行生成一个哈希映射:

    (defmacro to-rows [& args]
      (let [names (mapv name args)]
        `(map (fn [& vs#] (zipmap ~names vs#)) ~@args)))
    
    => ({"nums" 1, "chars" \a, "bools" true}
        {"nums" 2, "chars" \b, "bools" false}
        {"nums" 3, "chars" \c, "bools" nil})
    

    然后您需要使用 data.csv 或类似代码将其写入文件。

    要查看to-rows 扩展为什么,您可以使用macroexpand。这是在编译时生成的代码,将在运行时求值。它会在编译时获取符号names,但会在运行时发出将在其绑定的上工作的代码。

    (macroexpand '(to-rows x y z))
    => (clojure.core/cons ["x" "y" "z"] (clojure.core/map clojure.core/vector x y z))
    

    顺便说一句,我假设您没有在let 绑定中输入数千个文字值。我认为这回答了所提出的问题,但可能有比这更直接的方法。

    【讨论】:

    • 感谢您指出获取 var 名称和 var 值的方法,即“to-rows”宏,是的,我确实想将它们写入由 ',' 分隔的文件。让我试一试。
    【解决方案2】:

    您可以先将变量名称及其值捕获到映射中:

    (defmacro name-map
      [& xs]
      (let [args-list# (cons 'list (map (juxt (comp keyword str) identity) xs))]
        `(into {} ~args-list#)))
    

    如果您将 var 名称传递给宏,

    (let [aa 11
          bb 22
          cc 33]
       (name-map aa bb cc))
    

    它会为您提供一张地图,然后您可以将其用于任何进一步的处理:

    => {:aa 11, :bb 22, :cc 33}
    (def result *1)
    (run! 
      (fn [[k v]] (println (str "spit file_" (name k) " value: " v)))
      result)
    =>
    spit file_aa value: 11
    spit file_bb value: 22
    spit file_cc value: 33
    

    编辑:刚刚注意到它类似于泰勒的宏。不同之处在于它也适用于原始类型,而 Taylor 的适用于原始数据(变量解析为集合)。

    【讨论】:

      【解决方案3】:

      我认为您正在寻找函数name。演示:

      user=> (defmacro write-columns [& columns]
               (let [names (map name columns)]
                 `(str ~@names)))
      #'user/write-columns
      user=> (write-columns a b c)
      "abc"
      

      【讨论】:

      • 我认为他们的问题更多在于弄清楚宏观时间和运行时发生了什么。
      猜你喜欢
      • 2023-03-13
      • 1970-01-01
      • 1970-01-01
      • 2012-10-04
      • 1970-01-01
      • 2011-05-16
      • 1970-01-01
      • 1970-01-01
      • 2023-02-04
      相关资源
      最近更新 更多