sequence関係のユーティリティ関数で自分がよく知らないものをまとめておく。
昔はindexed
っていうそのまんまな関数があったけど、1.3くらいからなくなっていた。今はmap-indexed
っていう関数を使う。よく使うのはこんな感じの使い方。
(map-indexed #(vector %1 %2) ["a" "b" "c" "d" "e"])
; ([0 "a"] [1 "b"] [2 "c"] [3 "d"] [4 "e"])
reduceしていくときの中間値みたいなのも戻り値の要素に加えておいてくれる。Rでいうところにcumsumっぽいことが簡単にできて便利。reduce likeな関数なので初期値ももちろん取れる。
(reductions + [1 2 3]) ; (1 3 6)
intoも似たようなことしなかったっけ...と思って違いを押さえる。intoはvectorをつなげていく。zipmapは最初のvectorをkeyにして二つ目をvalueにしたmapを返す。keyが被ってたら後ろのが生き残るらしい。intoとconcatは何が違うねん、、、と思ったけど、concatのほうはlazylistを返すそうなので、lazyなのが欲しいかそうでないかでintoとconcatを使い分けるとよいのかな。
(zipmap [:a :b :c :d :e] [1 2 3 4 5]) ; {:e 5, :d 4, :c 3, :b 2, :a 1}
(zipmap [:a :a :b :b :c] [1 2 3 4 5]) ; {:c 5, :b 4, :a 2}
(into [:a :a :b :b :c] [1 2 3 4 5]) ; [:a :a :b :b :c 1 2 3 4 5]
(concat [:a :a :b :b :c] [1 2 3 4 5]) ; (:a :a :b :b :c 1 2 3 4 5)
(map vector [:a :b :c] [:x :y :z]) ; ([:a :x] [:b :y] [:c :z])
第二引数に渡したcollectionを第一引数fに渡したに適用した結果で分類してmapで返す。キーは当然ながらfを適用した結果。
(group-by count ["a" "as" "asd" "aa" "asdf" "qwer"]) ; {1 ["a"], 2 ["as" "aa"], 3 ["asd"], 4 ["asdf" "qwer"]}
(group-by odd? (range 10)) ; {false [0 2 4 6 8], true [1 3 5 7 9]}
(group-by :user_id [{:user_id 1 :uri "/"}
{:user_id 2 :uri "/foo"}
{:user_id 1 :uri "/account"}]) ; {1 [{:user_id 1, :uri "/"} {:user_id 1, :uri "/account"}], 2 [{:user_id 2, :uri "/foo"}]}
exampleを見たら「それfilterと何が違うん...?」って感じだったけど、keepのほうはtrue falseでfilteringするのではなくnilかどうかでやる関数らしい。ちなみに似た関数にkeep-indexedというのがあって、これは無名関数のほうにindexも使えるようにした感じのものらしい。
(keep #(if (odd? %) %) (range 10)) ; (1 3 5 7 9)
(filter #(if (odd? %) %) (range 10)) ; (1 3 5 7 9)
(keep-indexed #(if (odd? %1) %2) (range 0 10)) ; (1 3 5 7 9)
(keep-indexed (fn [idx v] (if (pos? v) idx)) [-9 0 29 -7 45 3 -8]) ; (2 4 5)
指定した位置でcollectionをぶったぎる。split-withだと指定した条件を初めて満たすところでぶったぎる。take nとdrop nの組み合わせみたいなもの。
(split-at 2 [1 2 3 4 5]) ; [(1 2) (3 4 5)]
(split-with (partial >= 3) [1 2 3 4 5])
いかにもclojureらしい関数。無限リストを生み出すのだが、関数fと初期値xに関してx、(f x)、(f (f x))、(f (f (f x)))...というようなsequenceを作る。使うときにはtakeとかと合わせて、という感じかな。
(take 3 (iterate inc 0)) ; (0 1 2)
複数のcollectionを引数に取り、それぞれをmapした結果を連結する。下のmapしてapply concatと等価、だと思う。
(mapcat reverse [[3 2 1 0] [6 5 4] [9 8 7]]) ; (0 1 2 3 4 5 6 7 8 9)
(apply concat (map reverse [[3 2 1 0] [6 5 4] [9 8 7]])) ; (0 1 2 3 4 5 6 7 8 9)
2つ、またはそれ以上のsequenceを受け取り、それらの要素を交互に持つようなlazy listを返す。これは例を見ればすぐ分かる。
(interleave '[1 3 5] '[2 4 6]) ; (1 2 3 4 5 6)
(interleave '[1 4 7] '[2 5 8] '[3 6 9]) ; (1 2 3 4 5 6 7 8 9)
リストの間に一個置きに何か要素を差し挟む関数。my-stringsの例は別途他の関数があるのでそちらを使えばいいが、まぁ例ということで。
(interpose 3 '[1 1 1 1]) ; (1 3 1 3 1 3 1)
(def my-strings ["one" "two" "three"])
(interpose ", " my-strings) ; ("one" ", " "two" ", " "three")
(apply str (interpose ", " my-strings)) "one, two, three"
わかりやすいです.
into の使い所について補足させてください.
into は第一引数のコレクションに, 第二引数のシーケンスの要素全てを, 元のコレクションにとって自然な形で追加 (conj) してくれる関数です.
コレクションがリストやベクタだとあまりありがたみ分かりませんが, ハッシュマップや集合では大変ありがたいです.
(into %1 %2)
は,(reduce conj %1 %2)
つまり%1
に%2
の要素を順にconj
したものと同等, さらに噛み砕くと...(conj (conj %1 %2の最初の要素) %2の二番目の要素)...
と同等です.