如何找到绑定形式的所有符号?

时间:2016-06-30 16:37:10

标签: clojure

给定[{a :a} {b :b}]之类的绑定表格如何找到所有符号? (a b)

2 个答案:

答案 0 :(得分:2)

天真的方法是将绑定表单视为一些嵌套集合,找到该集合中的所有符号,并返回这些符号的序列:

(defn symbols [x]
  (filter symbol? (tree-seq coll? seq x)))

(symbols '[{a :a} {b :b}])
;;=> (a b)

然而,作为@amalloy noted,这并不适用于所有情况。以下是一些示例,其中symbols的上述实现会产生不良结果:

;; & isn't actually bound to anything
(symbols '[foo & bar])
;;=> (foo & bar)

;; duplicates
(symbols '{x :foo :or {x :bar}})
;;=> (x x)

;; keys and default values are evaluated, not bound
(symbols '{x (keyword "foo") :or {x (keyword 'bar)}})
;;=> (x keyword x keyword quote bar)

;; namespaced keywords and symbols don't work
(symbols '{:keys [::foo :bar/baz qux/quux]})
;;=> (qux/quux)

他建议使用内置的destructure函数,但正如他在答案中所证明的那样,这会导致一些垃圾出现在结果中:

(take-nth 2 (destructure '[{:keys [x]} (last y)]))
;;=> (map__10938 map__10938 x)

虽然这个技术上给出了Clojure将绑定的符号列表,但map__10938只是一个实现工件,与解构语言本身无关。

值得庆幸的是,手动解析绑定表单并组装一组符号(从原始绑定表单中获取)并不太难:

(require '[clojure.set :as set])

(defn symbols [binding]
  (cond
    (symbol? binding)
    #{binding}

    (vector? binding)
    (apply set/union (map symbols (remove #{'& :as} binding)))

    (map? binding)
    (apply set/union
           (for [[k v] binding]
             (case k
               :or #{}
               :as #{v}
               (:keys :strs :syms) (set (map (comp symbol name) v))
               (symbols k))))))

答案 1 :(得分:1)

更好的是使用~/.gradle/init.gradle,它理解哪些符号是将被绑定的名称,而不是将被拆分的值。例如,考虑:

clojure.core/destructure

在这种情况下,您几乎肯定不希望在符号列表中包含(let [{:keys [x]} (last y)] x) ,假设您使用它来更好地理解解构规范。如果你打电话给last,它会告诉你究竟什么名字将被绑定到什么值:

destructure

现在一方面你得到一个实际上没有被调用者输入的符号,但这可能仍然有用,因为它告诉你Clojure实际上会做什么来处理这个user> (destructure '[{:keys [x]} (last y)]) [map__10938 (last y) map__10938 (if (clojure.core/seq? map__10938) (clojure.lang.PersistentHashMap/create (clojure.core/seq map__10938)) map__10938) x (clojure.core/get map__10938 :x)] 表达式。要获得左侧,即要绑定的名称,您可以使用

let