簡體   English   中英

如何從 Clojure 中的字符串中獲取變量的值?

[英]How can I get the value of a variable from a string in Clojure?

我在 clojure 做一個小項目,我想知道是否有這樣的事情:

(let [myvar "hello"] (println (read-var "myvar")))

其中“read-var” function 發現有一個名稱作為字符串傳遞的變量並返回它的值。

我發現這個加載字符串function 但它似乎不適用於 let 綁定。

謝謝!

我想說,如果你需要這種行為,你可能做得不對。 事實上,我什至無法想象為什么有人要在實踐中這樣做

但有辦法

clojure 宏具有特殊的隱式參數,稱為&env ,允許您獲取本地綁定。 因此,您可以在運行時將此功能用於本地變量解析:

(defmacro get-env []
  (into {} (map (juxt str identity)) (keys &env)))

請注意,此宏不需要在編譯時知道您想要的 var 名稱,它只是將綁定從宏 scope 提升到運行時 scope:

(let [x 10]
  (let [y 20]
    (get-env)))
;;=> {"x" 10, "y" 20}

(let [a 10
      b 20
      c 30
      env (get-env)]
  [a b c env])
;;=> [10 20 30 {"a" 10, "b" 20, "c" 30}]

甚至這個

(let [a 10
      b 20
      c 30
      env (get-env)]
  (get-env))
;;=> {"a" 10, "b" 20, "c" 30, "env" {"a" 10, "b" 20, "c" 30}}

(let [x 10] (println ((get-env) "x")))
;;=> 10
;;   nil

所以行為是動態的,可以用這個有趣的例子來展示:

(defn guess-my-bindings [guesses]
  (let [a 10
        b 20
        c 30]
    (mapv #((get-env) % ::bad-luck!) guesses)))

user> (guess-my-bindings ["a" "zee" "c"])
;;=> [10 :user/bad-luck! 30]

但請注意,此get-env效果僅限於在展開時有效的綁定。 例如:

(let [x 10
      y 20
      f (fn [] (let [z 30]
                 (get-env)))]
  (f))
;;=> {"x" 10, "y" 20, "z" 30} ;; ok

(def f (let [x 10
             y 20]
         (fn [] (let [z 30]
                  (get-env)))))

(f)
;;=> {"x" 10, "y" 20, "z" 30} ;; ok

(let [qwe 999]
  (f))
;;=> {"x" 10, "y" 20, "z" 30} ;; oops: no qwe binding 

如果read-var必須是 function,我不知道有什么方法可以做到這一點。 如果read-var是一個宏並且它的參數是一個字符串文字,那么可以實現read-var以便您編寫的代碼可以工作。 另一種方法是構建一個使用eval的宏read-var ,但這也是不可能的,因為eval無法訪問本地綁定,如本答案中所述。

我能想到的最接近的(i) read-var實現為 function 和(ii)讓您將運行時值作為 arguments 傳遞給read-var如下:

(def ^:dynamic context {})

(defmacro with-readable-vars [symbols & body]
  `(binding [context (merge context ~(zipmap (map str symbols) symbols))]
     ~@body))

(defn read-var [varname]
  (get context varname))

現在您可以使用此代碼

(let [myvar "hello"]
  (with-readable-vars [myvar]
    (println (read-var "myvar")))) ;; Prints hello

與您的代碼相比,不同之處在於您必須使用with-readable-vars宏聲明應該可讀的變量。 顯然,如果您願意,您可以構建另一個結合了letwith-readable-vars宏:

(defmacro readable-let [bindings & body]
  `(let ~bindings
     (with-readable-vars ~(vec (take-nth 2 bindings))
       ~@body)))

(readable-let [myvar "hello"]
  (println (read-var "myvar")))

上面的代碼假定您沒有為綁定使用解構等高級功能。

這里很簡單:

  (let [myvar "hello"]
    (println myvar))
  ;=> hello

請參閱此示例項目,尤其是。 文件清單。


如果您真的想將變量的名稱作為字符串傳遞,則需要eval function:

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [schema.core :as s]
  ))

(dotest
  (let [mydata "hello"]
    (is= "hello" mydata) ; works
  ))

(def myVar "hello") ; creates a Clojure 'Var':  tst.demo.coore/myVar

(dotest
   ; will fail if don't use fully-qualified namespace in string
    (let [parsed (clojure.edn/read-string "(println :with-eval tst.demo.core/myVar)")]
      (eval parsed)
      ;=> `:with-eval hello`
  ))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM