2013/07/23

Clojure(1.3-1.4)のリスト/ベクタ(シーケンス)操作関数一覧

 何回使っても、気づいたら、忘れてしまうリスト/ベクタ系関数について調べたメモ。
Clojureでは、プログラムの大半はシーケンス操作になる可能性が高い為か、Clojureのシーケンス操作関数は、かなり充実していますが、たくさんありすぎて、使いこなせているか不安になります。オフィシャルのリファレンスに関数一覧が乗っていますが、リストとベクタの操作に限定してまとめました。
 ちなみに、リスト, ベクタ ⊆ シーケンスです。
zip(木構造), hashmap等は扱いません。

以下の9種類に大別。62個について調べました(まだあるかもしれません)。

  1. 生成系(7)
  2. cons系(3)
  3. getter系(21)
  4. observer系(8)
  5. map系(6)
  6. reduce系(2)
  7. filter系(3)
  8. リスト再構築(8)
  9. リスト並び替え(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

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])

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])

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



observer系

リストから情報を取り出す。

count

 リストの要素を数える。lengthではなく、countとなっていて、「長さを計算する」という処理であることを明示的に表している点が良いですね。
(count [1 2 3]) => 3
(count '()) => 0

empty?

 リスト/ベクタが空かどうかを判定する。空でtrue。そうでなければfalse。Schemeにnull?という空リストの判定をする関数があって、Clojureにもnil?という似たような関数がありますが、nil?ではカラリストの判定はできない場合があるので注意。
(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

some/not-any?

 与えられた要素を満たす要素が存在するか判定する。/与えられた条件を満たさない要素が存在しないか判定する。 
(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



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)

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)

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)

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-indexed

 keepした要素にインデックスを付ける。
(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

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])

remove

 与えられた関数がtrueを返すものを取り除く。filterの反対です。
(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)



再構築

シーケンスの要素を分割したり結合したりする。

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)

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-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)]

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-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))


 一通り調べてみた結果、多くを使いこなしてないことが判明しました。orz ...

 (使ってなかったけど)これから使いそうなものから、どのように使うかよくわからないものまで、たくさんあります。maxやminのような引数の中から最大値、最小値を求めるような関数もリスト操作関数に近いですが、厳密にはリストを扱っているわけではないので、今回は省きました。全部網羅できると、スッキリします。

0 件のコメント :