我在 clojure 中做了一个小项目,我想知道是否有这样的东西:
(let [myvar "hello"] (println (read-var "myvar")))
Run Code Online (Sandbox Code Playgroud)
其中“read-var”函数发现有一个名称作为字符串传递的变量并返回它的值。
我发现了这个load-string函数,但它似乎不适用于 let 绑定。
谢谢!
我会说,如果你需要这种行为,你可能做得不对。事实上,我什至无法想象为什么有人要在实践中这样做
但有办法
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)