Clojure のツリー ビジターに関するこの記事を読んでいて、以下の例に出くわしました。
(def data [[1 :foo] [2 [3 [4 "abc"]] 5]])
(walk/postwalk #(do (println "visiting:" %) %) data)
ポストウォークの外形は何をしているの?その有用性が理解できません。postwalk はどのように、そしてなぜ使用されるのですか? 説明をいただければ幸いです。
#()が何を意味するのか、do(form1 form2)の目的が何を意味するのかわからないので、両方に答えます。
#()
匿名関数を宣言するための省略形です。匿名関数は、ある関数を別の関数に渡すときに役立ちます。
説明のために、これをreplで見てください
; define an anonymous function
user=> #(+ %1 %2)
#<user$eval68$fn__69 user$eval68$fn__69@9fe84e>
; is equivalent to
user => (fn [a b] (+ a b))
#<user$eval1951$fn__1952 user$eval1951$fn__1952@118bd3c>
; furthermore, you could then assign your anonymous function to a var
(def f #(+ %1 %2))
; is equivalent to
(defn f [a b] (+ a b))
user=> (#(+ %1 %2) 1 2)
3
user=> (f 1 2)
3
は%n
、関数の位置引数への引数を参照します。ここで、は1から始まる引数をn
意味nth
します。さらに簡略化すると、%を使用して、単一引数の無名関数で適切に機能する最初の引数を参照できます。これはあなたの例にあるものです。
したがって、例は次のようになります
(def data [[1 :foo] [2 [3 [4 "abc"]] 5]])
(defn f [x] (do (println "visiting:" x) x))
(walk/postwalk f data)
ここdo
に特別なフォームがあります。これはドキュメントからのものです。
(do exprs *)式を順番に評価し、最後の値を返します。式が指定されていない場合は、nilを返します。
実際defn
、すでに暗黙のdoがあるので、上記の例では実際にはdoを必要としません...
; your example is equivalent to:
(def data [[1 :foo] [2 [3 [4 "abc"]] 5]])
(defn f [x] (println "visiting:" x) x)
(walk/postwalk f data)
今日も同じ質問があり、このネクロトピックを見つけました。遅い方が新しいのかもしれません。clojure APIリファレンスでこれを見つけてください:
postwalk 関数 使用法: (postwalk f form) form の深さ優先の後順トラバーサルを実行します。各サブフォームで f を呼び出し、元の代わりに f の戻り値を使用します。sorted-map-by を除くすべての Clojure データ構造を認識します。doall と同様に seq を消費します。
また、このclojuredocs の例は物事をより明確にします:
(use 'clojure.walk)
(let [counter (atom -1)]
(postwalk (fn [x]
[(swap! counter inc) x])
{:a 1 :b 2}))
=> [6 {2 [[0 :a] [1 1]], 5 [[3 :b] [4 2]]}]
したがって、postwalk はすべてのサブフォームを関数の結果に置き換えます。ネストされた構造を新しい値で更新するために使用されます。そのため、結果の関数の末尾に x または % が含まれます。結果に入力を追加すると、さらにネストされた構造になります。
上の例では、ネストされたマップ構造の深さを通り抜ける様子が見られます。最初にマップの最も深い要素に移動し、次に高いレベルに移動し、次のフォームに潜伏し、再び上昇してフォーム全体で最後の移動を終了します。