Как связать имя и значение var в макросе clojure? - PullRequest
0 голосов
/ 11 сентября 2018

Допустим, у меня есть несколько (более 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 , не может быть привязан к дате-температуре в форме LET , (symbol vname#) возвращает ноль.

Ответы [ 3 ]

0 голосов
/ 11 сентября 2018

Похоже, что вы хотите записать файл значений с разделителями, используя имена привязок и их значения внутри 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 или аналогичный код.

Чтобы увидеть, на что распространяется * 1012, вы можете использовать macroexpand. Это код , сгенерированный во время компиляции, который будет оценен во время выполнения. Он выполняет работу по получению символа names во время компиляции, но выдает код, который будет работать с их связанными значениями во время выполнения.

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

Кроме того, я предполагаю, что вы не вводите тысячи буквенных значений в let привязках. Я думаю, что это отвечает на поставленный вопрос, но, вероятно, может быть более прямой подход, чем этот.

0 голосов
/ 13 октября 2018

Вы можете сначала записать имена переменных и их значения в карту:

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

Если вы передадите имена переменных макросу,

(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

Редактировать: Просто заметил, что это похоже на макрос Тейлора. Разница в том, что этот работает и с примитивными типами, в то время как работы Тейлора работают с исходными данными (переменные разрешаются в коллекции).

0 голосов
/ 11 сентября 2018

Я думаю, что вы ищете функцию name. Для демонстрации:

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