如何从 Clojure 中的字符串获取变量的值?

MrB*_*ckV 0 clojure

我在 clojure 中做了一个小项目,我想知道是否有这样的东西:

(let [myvar "hello"] (println (read-var "myvar")))
Run Code Online (Sandbox Code Playgroud)

其中“read-var”函数发现有一个名称作为字符串传递的变量并返回它的值。

我发现了这个load-string函数,但它似乎不适用于 let 绑定。

谢谢!

lee*_*ski 6

我会说,如果你需要这种行为,你可能做得不对。事实上,我什至无法想象为什么有人要在实践中这样做

但有办法

clojure 宏具有特殊的隐式参数,称为&env,允许您获取本地绑定。因此,您可以在运行时将此功能用于本地 vars 解析:

(defmacro get-env []
  (into {} (map (juxt str identity)) (keys &env)))
Run Code Online (Sandbox Code Playgroud)

请注意,此宏不需要在编译时知道您想要的 var 名称,它只是将绑定从宏范围提升到运行时范围:

(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}]
Run Code Online (Sandbox Code Playgroud)

甚至这个

(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
Run Code Online (Sandbox Code Playgroud)

所以行为是动态的,这可以用这个有趣的例子来展示:

(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]
Run Code Online (Sandbox Code Playgroud)

但请注意,此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
Run Code Online (Sandbox Code Playgroud)

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