2016-06-30 20 views

答えて

2

はナイーブなアプローチは、単にいくつかのネストされたコレクションとして結合フォームを処理することで、そのコレクション内のすべてのシンボルを見つけ、それらのシンボルのシーケンスを返します:

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

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

しかし、@amalloy noted、このようすべての場合に機能しません。

;; & 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機能を使用することを提案しているが、彼は彼の答えに示されているように、これはいくつかのゴミがに表示する原因:ここではsymbolsの上記の実装は望ましくない結果を与えるいくつかの例があります結果:

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

この技術的にている間はmap__10938はちょうど実装の成果物であることを、Clojureのは、結合するシンボルのリストを与え、非構造言語自体とは何の関係もありません。

はありがたいことに、それは手動で結合フォームを解析し、束縛されるだろう、元の結合形式、から取られたシンボルのセットを組み立てるにはあまりにも難しいことではありません。

(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)))))) 
+0

素晴らしい、ありがとう! –

+0

これは私にはかなり良く見えます。あなたは、 '' case k:や#{}の場合、#{v}、(:keys:strs:syms)(set ...)、(symbols k) – amalloy

+0

@amalloyクール、ありがとう! :)私は、あなたが「ケース」でそれを行うことができるかどうかを知りませんでした。 –

1

はるかに良いclojure.core/destructureを使用することで、分割される値ではなく、どのシンボルが束縛される名前であるかを理解します。たとえば、考えてみます。その意味で

(let [{:keys [x]} (last y)] 
    x) 

を、あなたはほぼ確実にあなたがより良い非構造仕様を理解するために、これを使用していることを前提とし、シンボルのリストにlastを含める必要はありません。そして、あなたはdestructureを呼び出す場合、それは名前がどの値にバインドされます正確に何を説明します:

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の式を処理するためにClojureが実際に何を行うのかを示すため、まだ便利です。左辺、つまり束縛される名前を取得するには、

(take-nth 2 (destructure '[{:keys [x]} (last y)])) 
+0

彼はバインディングの両方の半分を見ることを要求していません(例えば、 '[{:keys [x]}(last y)]');彼はバインディングの*左*側にシンボルを見つけるだけです。たとえば、式は '({[{a:{b:b}] [{:a:foo} {:b:bar}]] [ab])'のように見えるかもしれません。彼の質問だけで、彼は '[{a:a} {b:b}]'の部分でシンボルを探したいと思っています。 –

+0

左半分しか持っていない場合でも、束縛される名前ではない記号が入っていることがあります。たとえば、 '{:keys [x]:or {x(range 10)}}' - 本当にこの関数から 'range'が返されますか? – amalloy

+0

ああ、クール!私は[Clojure destructuring guide](http://clojure.org/guides/destructuring)に戻ってきました。私が知らなかった破壊をすることができるたくさんのものがあるようです。私はそれらの複雑さを反映するために私の答えを編集しました。 –

関連する問題