Clojureでは、プログラムの大半はシーケンス操作になる可能性が高い為か、Clojureのシーケンス操作関数は、かなり充実していますが、たくさんありすぎて、使いこなせているか不安になります。オフィシャルのリファレンスに関数一覧が乗っていますが、リストとベクタの操作に限定してまとめました。
ちなみに、リスト, ベクタ ⊆ シーケンスです。
zip(木構造), hashmap等は扱いません。
以下の9種類に大別。62個について調べました(まだあるかもしれません)。
- 生成系(7)
- cons系(3)
- getter系(21)
- observer系(8)
- map系(6)
- reduce系(2)
- filter系(3)
- リスト再構築(8)
- リスト並び替え(4)
生成系
遅延シーケンスを生成する。
list
引数列のリストを返す。
(list 1 2 3) => (1 2 3)
(list "a" 2 [72 71] 'u) => ("a" 2 [72 71] u)
range
自然数列のリストを生成する。
(range) => (0 1 2 3 ... 無限に続く。
(range 1 10) => (0 1 2 3 4 5 6 7 8 9 10)
repeat
引数に与えられた要素を繰り返したリストを返す。
(repeat "val") => ("val" "val" "val" "val" ... ... 無限に続く。
(repeat 4 "val") => ("val" "val" "val" "val")
repeatedly
引数に与えられた関数の実行結果を要素とするリストです。repeatが動詞でrepeatedlyが副詞なのは、それぞれ名詞と動詞を引数に持つからですか。
(repeatedly #(rand-int 10)) => (4 5 8 8 6 2 0 7 7 2 ... ... 無限に続く。
cycle
引数に与えられたリストの要素を繰り返したリストを返す。
(repeat [1 2 3]) => (1 2 3 1 2 3 ... ... 無限に続く。
iterate
n番目の要素が、xに対してn回fを適用したものであるリストを返す。x, f(x), f(f(x)) ... のリストを返す。
(iterate dec 0) => (0 -1 -2 -3 -4 -5 -6 ... ... 無限に続く。
for
リスト内包表記(List Comprehension)です。:letは、内包表記内での変数定義、:whenはフィルターです。表記内で与えられたリストから別のリストを生成します。PythonやHaskellのList Comprehensionとくらべて、理解しづらいです。
(for [x [1 2 3] y '(a b c)] [x y]) => ([1 a] [1 b] [1 c] [2 a] [2 b] [2 c] [3 a] [3 b] [3 c])
(for [x [1 2 3] :let [y (* x x)]] y) => (1 4 9)
(for [x [1 2 3 4 5] :when (even? x)] x) => (2 4)
seq
一部のデータ型をリストへキャストする。キャストできないものはエラー。seqは、遅延シーケンスではなく、通常のシーケンスを生成する。
(seq '()) => nil
(seq "abc") => (\a \b \c)
cons系
リストを構築する。
cons
先頭に与えられた要素を加えたリストを返す。
(cons 'a '(b c d)) => (a b c d)
(cons 1 [2 3 4]) => (1 2 3 4)
(cons [1] [2 3]) => ([1] 2 3)
conj
シーケンスに、要素を加えたものを返す。consとの違いは、consが必ずリストの先頭に加えられるのに対して、conjは、リストやベクタによって新たな要素が加えられる位置が変わってくるという点。一番追加しやすい場所に追加されるようです。要素の順番に依存しないシーケンスを扱う時に使います。
(conj 1 [2 3 4]) => [2 3 4 1]
(conj 1 '(2 3 4)) => (1 2 3 4)
concat
複数のシーケンスをすべて結合する。
(concat [1 2 3] [2 2 3]) => [1 2 3 2 2 3]
(concat [1] [2] [3]) => (1 2 3)
getter系
シーケンスから要素を取り出す。
first/ffirst
シーケンスの先頭の要素を取り出す。古いLisp系言語のcarに相当します。/シーケンスの先頭の先頭の要素を取り出す。古いLisp系言語のcaarに相当します。
(first [3 2 1]) => 3
(ffirst [[1 2 3] [2 2 3]]) => 1
(ffirst [[1 2 3] [2 2 3]]) => 1
second
シーケンスの二番目の要素を取り出す。古いLisp系言語のcadrに相当します。
(second [1 2 3]) => 2(second [1]) => nil
nth
シーケンスのn番目の要素を取り出す。インデックスは0から始まります。
(nth [1 2 3] 2) => 3(nth [1 2 3] 0) => 1
rand-nth
リストからランダムに要素を選んで取り出す。
(rand-nth [:a :b :c]) => :a(rand-nth [:a :b :c]) => :b
rest/ntresth
リストの先頭の要素を除いた残りの要素を取り出す。古いLisp系言語のcdrに相当します。/リストのn番目の要素?
(rest [1 2 3]) => [2 3](rest []) => []
next/nthnext
リストの先頭の要素を除いた残りの要素を取り出す。restとの違いは、リストの末尾に到達した時に、[]を返すのがrest、nilを返すのがnext。/リストのn番目までの要素を除いた残りの要素を取り出す。
(next [1 2 3 4]) => (2 3 4)
(next [1]) => nil
(nthnext [[1 2] [3 4 5] [6 7 8]] 2) => ([6 7 8])
(next [1 2 3 4]) => (2 3 4)
(next [1]) => nil
(nthnext [[1 2] [3 4 5] [6 7 8]] 2) => ([6 7 8])
fnext/nnext
先頭を除いたリストの先頭の要素を取り出す。(fnext x) = (first (next x))/リストの先頭2つの要素を除いた残りの要素を取り出す。(nnext x) = (next (next x))
(fnext [[1 2] [3 4 5]]) => [3 4 5]
(nnext [[1 2] [3 4 5] [6 7 8]]) => ([6 7 8])
(fnext [[1 2] [3 4 5]]) => [3 4 5]
(nnext [[1 2] [3 4 5] [6 7 8]]) => ([6 7 8])
take/take-while/take-last
先頭からn個要素を取り出す。/先頭から与えられた条件を満たさない要素になるまで取り出す。/末尾からn個要素を取り出す。
(take 3 (range 1 10)) => (1 2 3)(take-while odd? [1 3 5 6 7 8]) => (1 3 5)
(take-last 3 (range 1 10)) => (7 8 9)
drop/drop-while/drop-last
先頭からn個要素を省く。/先頭から与えられた条件を満たさない要素になるまで省く。/末尾からn個要素を省く。
(drop 3 (range 1 10)) => (4 5 6 7 8 9)(drop-while odd? [1 3 5 6 7 8]) => (6 7 8)
(drop-last 3 (range 1 10)) => (1 2 3 4 5 6)
last/butlast
最後の値を取り出す。/最後の値を取り除いたリストを返す。
(last [1 2 3 4 5]) => 5(butlast [1 2 3 4 5]) => (1 2 3 4)
when-first
リストの先頭の値をバインドする。
(when-first [a [1 2 3]] (* a 5)) => 5
(when-first [a [1 2 3]] (* a 5)) => 5
observer系
リストから情報を取り出す。count
リストの要素を数える。lengthではなく、countとなっていて、「長さを計算する」という処理であることを明示的に表している点が良いですね。
(count [1 2 3]) => 3
(count '()) => 0
(reductions * (range 1 11)) => (1 2 6 24 120 720 5040 40320 362880 3628800)
(partition-by odd? [1 1 2 4 5 3 4 4 2]) => ((1 1) (2 4) (5 3) (4 4 2))
(interpose 0 (range 10)) => (0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9)
(reverse [1 3 2 4 6 5 7 9 8]) => (8 9 7 5 6 4 2 3 1)
一通り調べてみた結果、多くを使いこなしてないことが判明しました。orz ...
(使ってなかったけど)これから使いそうなものから、どのように使うかよくわからないものまで、たくさんあります。maxやminのような引数の中から最大値、最小値を求めるような関数もリスト操作関数に近いですが、厳密にはリストを扱っているわけではないので、今回は省きました。全部網羅できると、スッキリします。
(count [1 2 3]) => 3
(count '()) => 0
empty?
リスト/ベクタが空かどうかを判定する。空でtrue。そうでなければfalse。Schemeにnull?という空リストの判定をする関数があって、Clojureにもnil?という似たような関数がありますが、nil?ではカラリストの判定はできない場合があるので注意。
(empty? []) => true
(empty? (range 1 10)) => false
(empty? []) => true
(empty? (range 1 10)) => false
empty/not-empty
空のシーケンスを空にして返す。/リストがからであればnilを返す。それ以外の場合は、そのリスト自身を返す。
(empty [1 2 3]) => []
(not-empty [1 2 3]) => [1 2 3]
(not-empty []) => nil
(empty [1 2 3]) => []
(not-empty [1 2 3]) => [1 2 3]
(not-empty []) => nil
some/not-any?
与えられた要素を満たす要素が存在するか判定する。/与えられた条件を満たさない要素が存在しないか判定する。
(some even? [1 1 2 3]) => true
(not-any? even? [1 1 2 3]) => false
(some even? [1 1 2 3]) => true
(not-any? even? [1 1 2 3]) => false
every?/not-every?
すべての要素が条件を満たすか判定する。/条件を満たさない要素が存在するか判定する。
(every? odd? [1 1 1 3]) => true
(not-every? odd? [1 1 2 3]) => true
(every? odd? [1 1 1 3]) => true
(not-every? odd? [1 1 2 3]) => true
map系
mapは、[a, b, c, d, .......]に対して、[f(a), f(b), f(c), f(d), ......]を求めるような関数。
map
与えられたリストに対して、それぞれの要素に与えられた関数を適用したリストを返す。
(map inc (range 1 10)) => (2 3 4 5 6 7 8 9 10)
(map even? (range 1 10)) => (false true false true false true false true false)
(map inc (range 1 10)) => (2 3 4 5 6 7 8 9 10)
(map even? (range 1 10)) => (false true false true false true false true false)
pmap
mapの処理を並列(parallel)に実行する。上手に使わないと逆に遅くなることもあります。
user=> (defn fibo[n] (if (< n 2) 1 (+ (fibo (- n 1)) (fibo (- n 2)))))
#'user/fibo
user=> (time (take 10 (doall (map fibo (range 1 30)))))
"Elapsed time: 363.431486 msecs"
(1 2 3 5 8 13 21 34 55 89)
user=> (time (take 10 (doall (pmap fibo (range 1 30)))))
"Elapsed time: 257.508792 msecs"
(1 2 3 5 8 13 21 34 55 89)
user=> (time (take 10 (doall (map inc (range 1 30)))))
"Elapsed time: 0.120742 msecs"
(2 3 4 5 6 7 8 9 10 11)
user=> (time (take 10 (doall (pmap inc (range 1 30)))))
"Elapsed time: 53.420469 msecs"
(2 3 4 5 6 7 8 9 10 11)
user=> (defn fibo[n] (if (< n 2) 1 (+ (fibo (- n 1)) (fibo (- n 2)))))
#'user/fibo
user=> (time (take 10 (doall (map fibo (range 1 30)))))
"Elapsed time: 363.431486 msecs"
(1 2 3 5 8 13 21 34 55 89)
user=> (time (take 10 (doall (pmap fibo (range 1 30)))))
"Elapsed time: 257.508792 msecs"
(1 2 3 5 8 13 21 34 55 89)
user=> (time (take 10 (doall (map inc (range 1 30)))))
"Elapsed time: 0.120742 msecs"
(2 3 4 5 6 7 8 9 10 11)
user=> (time (take 10 (doall (pmap inc (range 1 30)))))
"Elapsed time: 53.420469 msecs"
(2 3 4 5 6 7 8 9 10 11)
mapcat
引数のリストをconcatしたものに対してmapした結果を返す。
(mapcat sort [[4 5 1 6] [7 8 3 2] [10 9]]) => (1 4 5 6 2 3 7 8 9 10)
(mapcat reverse [[1 2 3] [7 8 9] [4 5 6]]) => (3 2 1 9 8 7 6 5 4)
(mapcat reverse [[1 2 3] [7 8 9] [4 5 6]]) => (3 2 1 9 8 7 6 5 4)
map-indexed
mapした要素にインデックスを付ける。
(map-indexed list (range 1 10)) => ((0 1) (1 2) (2 3) (3 4) (4 5) (5 6) (6 7) (7 8) (8 9))
(map-indexed + (reverse (range 1 10))) => (9 9 9 9 9 9 9 9 9)
keep
mapとほぼ同じ関数ですが、mapが返すリスト列は、nilを含むのに対して、keepは、nilを含まないリストを返します。つまり、 keep .... = (filter not-nil (map ....)のような感じでnil要素をフィルタリングします。
(keep next [[1] [] [2 3] [4 5 6] [] [7 8]]) => ((3) (5 6) (8))
(keep next [[1] [] [2 3] [4 5 6] [] [7 8]]) => ((3) (5 6) (8))
keep-indexed
keepした要素にインデックスを付ける。
(keep-indexed #(nnext (concat [%1] %2)) [[] [3 4] [] [] [5 6 7]]) => ((4) (6 7))
(keep-indexed #(nnext (concat [%1] %2)) [[] [3 4] [] [] [5 6 7]]) => ((4) (6 7))
reduce系
畳み込み処理をします。reduce
二項演算をする関数とリストを引数にとり、畳み込み処理をします。
(reduce + (range 1 11)) => 55
(reduce * (range 1 10)) => 362880
(reduce + (range 1 11)) => 55
(reduce * (range 1 10)) => 362880
reductions
n回目の畳み込み処理の結果からなるリストを返す。
(reductions + (range 1 11)) => (1 3 6 10 15 21 28 36 45 55)(reductions * (range 1 11)) => (1 2 6 24 120 720 5040 40320 362880 3628800)
filter系
リストから特定の要素を取り除きます。filter
与えられた関数がtrueを返すもののみからなるリストを返す。関数でフィルターします。
(filter even? (range 1 10)) => (2 4 6 8)
(filter next [[2 3] [] [2] [3 4]]) => ([2 3] [3 4])
(filter even? (range 1 10)) => (2 4 6 8)
(filter next [[2 3] [] [2] [3 4]]) => ([2 3] [3 4])
remove
与えられた関数がtrueを返すものを取り除く。filterの反対です。
(remove even? (range 1 10)) => (1 3 5 7 9)
(remove even? (range 1 10)) => (1 3 5 7 9)
(remove next [[2 3] [] [2] [3 4]]) => ([] [2])
distinct
重複する要素を取り除く。
(distinct [1 2 3 2 3 4 4 5]) => (1 2 3 4 5)
(distinct (take 100 (cycle [1 2 3]))) => (1 2 3)
(distinct [1 2 3 2 3 4 4 5]) => (1 2 3 4 5)
(distinct (take 100 (cycle [1 2 3]))) => (1 2 3)
再構築
シーケンスの要素を分割したり結合したりする。flatten
ネストしたシーケンスを平な線形のリストにする。複雑にネストしていても、再帰的に取り出します。
(flatten [[1 2 3] [4 5 6]]) => (1 2 3 4 5 6)
(flatten [[1 2 [3 [4 5 [6]] 7] 8] 9]) => (1 2 3 4 5 6 7 8 9)
(flatten [[1 2 3] [4 5 6]]) => (1 2 3 4 5 6)
(flatten [[1 2 [3 [4 5 [6]] 7] 8] 9]) => (1 2 3 4 5 6 7 8 9)
partition
リストをn個づつ分割する。割り切れずに、余った要素は切り捨てる。
(partition 4 (range 15)) => ((0 1 2 3) (4 5 6 7) (8 9 10 11))
(partition 4 3 (range 15)) => ((0 1 2 3) (3 4 5 6) (6 7 8 9) (9 10 11 12))
(partition 4 3 ["pad" "pad"] (range 15)) => ((0 1 2 3) (3 4 5 6) (6 7 8 9) (9 10 11 12) (12 13 14 "pad"))
(partition 4 (range 15)) => ((0 1 2 3) (4 5 6 7) (8 9 10 11))
(partition 4 3 (range 15)) => ((0 1 2 3) (3 4 5 6) (6 7 8 9) (9 10 11 12))
(partition 4 3 ["pad" "pad"] (range 15)) => ((0 1 2 3) (3 4 5 6) (6 7 8 9) (9 10 11 12) (12 13 14 "pad"))
partition-all/partition-by
すべて分割する。余った要素は最後に付加する。/条件がtrueとfalseが変わる瞬間で分割する。
(partition-all 4 (range 1 10)) => ((1 2 3 4) (5 6 7 8) (9))(partition-by odd? [1 1 2 4 5 3 4 4 2]) => ((1 1) (2 4) (5 3) (4 4 2))
split-at/split-with
n番目で分割する。/条件がfalseになったところで分割する。
(split-at 3 (range 1 10)) =>[(1 2 3) (4 5 6 7 8 9)]
(split-with #(< % 4) (range 1 10)) => [(1 2 3) (4 5 6 7 8 9)]
(split-at 3 (range 1 10)) =>[(1 2 3) (4 5 6 7 8 9)]
(split-with #(< % 4) (range 1 10)) => [(1 2 3) (4 5 6 7 8 9)]
interleave/interpose
2つのリストを折り込む。/リストのそれぞれの要素の間に要素を与える。
(interleave (range 1 10) (range 21 30)) => (1 21 2 22 3 23 4 24 5 25 6 26 7 27 8 28 9 29)(interpose 0 (range 10)) => (0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9)
並び替え系
シーケンスの要素を並び替える。
reverse
シーケンスの順序を反転する。
(reverse (range 1 10)) => (9 8 7 6 5 4 3 2 1)(reverse [1 3 2 4 6 5 7 9 8]) => (8 9 7 5 6 4 2 3 1)
shuffle
シーケンスをシャッフルする。
(shuffle (range 15)) => [6 7 13 11 5 12 3 1 8 4 10 9 2 14 0]sort
シーケンスを並び替える。要素を評価する関数を指定できる。
(sort (shuffle (range 1 10))) => (1 2 3 4 5 6 7 8 9)
(sort > (shuffle (range 1 10))) => (9 8 7 6 5 4 3 2 1)
(sort (shuffle (range 1 10))) => (1 2 3 4 5 6 7 8 9)
(sort > (shuffle (range 1 10))) => (9 8 7 6 5 4 3 2 1)
sort-by
シーケンスを並び替える。要素の並び替えにつかう値を指定できる。
(sort-by first (map list (shuffle (range 1 10)) (shuffle (range 1 10))))
=> ((1 3) (2 4) (3 9) (4 7) (5 1) (6 2) (7 6) (8 8) (9 5))
(sort-by second (map list (shuffle (range 1 10)) (shuffle (range 1 10))))
=> ((1 1) (5 2) (3 3) (4 4) (9 5) (2 6) (8 7) (7 8) (6 9))
(sort-by first (map list (shuffle (range 1 10)) (shuffle (range 1 10))))
=> ((1 3) (2 4) (3 9) (4 7) (5 1) (6 2) (7 6) (8 8) (9 5))
(sort-by second (map list (shuffle (range 1 10)) (shuffle (range 1 10))))
=> ((1 1) (5 2) (3 3) (4 4) (9 5) (2 6) (8 7) (7 8) (6 9))
一通り調べてみた結果、多くを使いこなしてないことが判明しました。orz ...
(使ってなかったけど)これから使いそうなものから、どのように使うかよくわからないものまで、たくさんあります。maxやminのような引数の中から最大値、最小値を求めるような関数もリスト操作関数に近いですが、厳密にはリストを扱っているわけではないので、今回は省きました。全部網羅できると、スッキリします。