我正在尝试编写一个函数eval-with-bindmap
它接受两个参数,一个要评估的表达式和一个符号到值的映射,这些值将在评估中充当绑定。我不能使用let
因为绑定可以在映射中按任何顺序排列(它应该同时适用于数组映射和哈希映射(,并且它还可能包含相互递归的函数绑定(如letfn
(。例如:
(eval-with-bindmap '(+ a b) '{a (+ 1 b) b 2}) => 5
关于如何处理这个问题的任何提示都会很棒,谢谢。
一些简单的递归处理可以工作(除非你的表单扩展真的很深,否则你应该考虑尾递归版本(
(defn eval-with-bindings [form bind]
(cond (seq? form) (apply (resolve (first form))
(map #(eval-with-bindings % bind) (rest form)))
(and (symbol? form) (contains? bind form)) (eval-with-bindings (bind form) bind)
:else form))
user> (eval-with-bindings '(+ a b) '{a (+ 1 b) b 2})
5
在无限循环 deps 的情况下,它只会与堆栈溢出一起下降:
user> (eval-with-bindings '(+ a b) '{a (+ 1 a) b 2})
StackOverflowError clojure.lang.AFn.applyToHelper (AFn.java:148)
说,更好的方法仍然是在let
中使用eval
,但以关心正确顺序的特殊方式将绑定映射处理成let
向量。
例如,假设您有一个地图{a (+ 1 b) b 2}
。对于每个键/表达式对,请检查表达式是否包含任何字母数字符号。b
将是我们的候选人。在这种情况下,您将b 2
移动到a (+ 1 b)
之前,依此类推。
是的,正确的算法可能有点复杂,但是您不会在进一步eval
时遇到任何麻烦。