(def a (edn/read-string "(+ 1 3)"))
; => (+ 1 3)
我如何评估这个结果列表?
(type (first a))
; => cljs.core/Symbol
(= (first a) '+)
; => true
我想更一般地说,我将如何从符号 -> 函数中获得。这是clojure的正常做法吗?我似乎找不到任何东西。也许我没有使用正确的术语进行搜索。
(def a (edn/read-string "(+ 1 3)"))
; => (+ 1 3)
我如何评估这个结果列表?
(type (first a))
; => cljs.core/Symbol
(= (first a) '+)
; => true
我想更一般地说,我将如何从符号 -> 函数中获得。这是clojure的正常做法吗?我似乎找不到任何东西。也许我没有使用正确的术语进行搜索。
你通常会使用eval
. 但是在 ClojureScript 中,您需要运行时可用的编译器和标准库。这只有在您使用自托管 ClojureScript 时才有可能。
如果您在自托管环境中(例如 Lumo、Planck、Replete、Klipse等),那么eval
将可以正常工作:
cljs.user=> (require '[clojure.edn :as edn])
nil
cljs.user=> (def a (edn/read-string "(+ 1 3)"))
#'cljs.user/a
cljs.user=> (eval a)
4
否则,您可以使用cljs.js
命名空间中的工具来访问自托管的 ClojureScript:
cljs.user=> (require 'cljs.js)
nil
cljs.user=> (cljs.js/eval (cljs.js/empty-state)
a {:eval cljs.js/js-eval :context :expr} prn)
{:value 4}
请注意,这样做需要考虑一些大小因素:ClojureScript 编译器将与您编译的工件一起带入目标环境,您还必须避免使用
:advanced
,确保整个cljs.core
标准库和相关元数据在运行时可用。
我的回答似乎只适用于 Clojure,而不适用于 ClojureScript。请参阅其他答案。
我想你可能正在寻找resolve
.
(defn my-simple-eval [expr]
; Cut the function symbol from the arguments
(let [[f & args] (edn/read-string expr)]
; Resolve f to a function then apply the supplied arguments to it
(apply (resolve f) args)))
(my-simple-eval "(+ 1 3)")
=> 4
但是,参数必须是裸数字才能起作用。如果你想允许子表达式,你可以让它递归:
(defn my-simple-eval-rec [expr]
(letfn [(rec [[f & args]]
(->> args
(map (fn [arg]
(if (list? arg)
(rec arg) ; Process the sub-expr
arg)))
(apply (resolve f))))]
(rec (edn/read-string expr))))
(my-simple-eval-rec "(+ 1 (+ 2 5))")
=> 8
如果这还不够,我不知道除了使用之外的任何方法eval
:
(def a (edn/read-string "(+ 1 3)"))
(eval a)
=> 4
或者,如果在扩展宏时数据可用,您可以将调用包装起来以read-string
正常解释数据:
(defmacro my-read-string [expr]
(edn/read-string expr))
(my-read-string "(+ 1 3)")
=> 4