Hatena::ブログ(Diary)

すにぺっと

2011-04-04

clojureプログラミング入門-9 シーケンスフィルタ、変換とリスト内包

| 23:54 | clojureプログラミング入門-9 シーケンスフィルタ、変換とリスト内包を含むブックマーク

シーケンスをフィルタする

元のシーケンスから要素を選び出すフィルタ関数をいくつかもっている。

(filter pred coll)

試しに偶数のみ、奇数のみのシーケンスをつくる。

user=> (defn wnum[] (iterate inc 1))
#'user/wnum
user=> (take 10 (filter even? (wnum)))
(2 4 6 8 10 12 14 16 18 20)
user=> (take 10 (filter odd? (wnum)))
(1 3 5 7 9 11 13 15 17 19)

述語が真を返しつづけるかぎり要素を取り出す

(take-while pred coll)
user=> (take-while (complement #{\a\i\u\e\o}) "hello world") ;1
(\h)
user=> (drop-while (complement #{\a\i\u\e\o}) "hello world");2
(\e \l \l \o \space \w \o \r \l \d)

complementは関数を渡されると、その反対の振る舞いをするような関数

返す関数

1.最初の母音に会うまでの文字列を返す。

また、drop-while関数は述語が偽になった時点でその後のシーケンスを返す。

2.最初の母音にあった後のシーケンスを返す。


シーケンスを分割するにはsplit-atとsplit-withを使う。

user=> (split-at 3 (range 10))
[(0 1 2) (3 4 5 6 7 8 9)]
user=> (split-with #(< % 10) (range 20))
[(0 1 2 3 4 5 6 7 8 9) (10 11 12 13 14 15 16 17 18 19)]

次はシーケンスに対する述語の説明。

user=> (every? odd? [1 3 5]) ;1
true
user=> (every? odd? [1 3 5 6])  ;2
false
user=> (some odd? [1 2 4]) ;3
true
user=> (some odd? [2 4 6]) ;4
nil

1,2.シーケンスすべてが奇数ならtrue.

3,4.シーケンスのどれかが奇数ならtrue.

ちなみに、someは常にtrue/falseを返すわけではなく、

1番目の引数が返した値を返すので注意。


シーケンスの変換

よく使われるのはmap.

(map f coll)

元コレクションcoll、関数fをとり、fをcollの各要素に適用した結果を

シーケンスで返す。

これはscalaとかpythonとかでもよく似た関数はある。

user=>  (map #(format "hello,%s" %) ["taro" "hanako" "yoshio"])
("hello,taro" "hello,hanako" "hello,yoshio")

あとはreduce.これもscalaとかでよく使う。

(reduce f cpll)

user=> (defn add [ x y ] (+ x y))
#'user/add
user=> (reduce add [1 2 3 4 5])
15

コレクションの最初、2番目の要素を第1引数に渡す。

その結果と3番目の要素・・・と最後まで渡していく。


コレクションのソートは

(sort comp? coll)

(sort-by a-fn comp? coll)

を使う。

user=> (sort [1 5 2 8  ])  
(1 2  5 8)
user=>  (sort-by #(.toString %) [23 4 9 33 111])
(111 23 33 4 9)

リスト内包

やっぱりclojureにもあったリスト内包。

これは

・入力となるリスト(複数可能)

・入力リストの各要素に束縛される変数

・要素に適用する述語

・述語の満たす入力リストの要素から出力リストを創りだす式

(for [binding-form coll-expr filter-expr? ...] expr)

mapやfilterより汎用性が高いのが特徴。

user=> (for [w ["taro" "hanako" "takeshi"]] (format "hello,%s" w))
("hello,taro" "hello,hanako" "hello,takeshi")

user=> (for [w ["taro" "hanako" "takeshi"] :when  (if (> (count w) 4) w)] (format "hello,%s" w))
("hello,hanako" "hello,takeshi")

コレクション要素をwに束縛し、述語に渡す。

:whenを使用すれば、フィルタをかけることもできる。

複数の束縛式もOK。

user=> (for [file "ABCDEFGH" rank (range 1 9)] (format "%c%d" file rank))
("A1" "A2" "A3" "A4" "A5" "A6" "A7" "A8" "B1" "B2" "B3" "B4" "B5" "B6" "B7" "B8" "C1" "C2" "C3" "C4" "C5" "C6" "C7" "C8" "D1" "D2" "D3" "D4" "D5" "D6" "D7" "D8" "E1" "E2" "E3" "E4" "E5" "E6" "E7" "E8" "F1" "F2" "F3" "F4" "F5" "F6" "F7" "F8" "G1" "G2" "G3" "G4" "G5" "G6" "G7" "G8" "H1" "H2" "H3" "H4" "H5" "H6" "H7" "H8")

ほとんどのシーケンス関数は、

要素が実際につかわれるまでシーケンスを走査しないらしい。

次は遅延シーケンスと無限シーケンス。

しかしなかなか慣れないな・・・

いっぱい書いて覚えるしかない。


プログラミングClojure
プログラミングClojure
posted with amazlet at 11.03.31
Stuart Halloway
オーム社
売り上げランキング: 174593

clojureプログラミング入門-8 シーケンスライブラリを使う

| 12:55 | clojureプログラミング入門-8  シーケンスライブラリを使うを含むブックマーク

シーケンスライブラリを使う

clojureのシーケンスライブラリは機能が豊富。

いろいろできるが、基本的には

・シーケンス生成

・シーケンスをフィルタ

・シーケンスにたいする述語

・シーケンスを変換する

というカテゴリに分類できる。


シーケンス生成

(range start? end step?)

で生成可能。

startとからstep毎加算しながらendまでのシーケンスを作成。

user=> (range 10) 
(0 1 2 3 4 5 6 7 8 9)
user=> (range 10 20) 
(10 11 12 13 14 15 16 17 18 19)
user=> (range 10 20 2) 
(10 12 14 16 18)

repeatは指定した回数分要素を繰り返したシーケンスを作成。

user=> (repeat 5 "hello")
("hello" "hello" "hello" "hello" "hello")

(iterate f x)

iterateはxを初期値として値にfを適用して次の値を計算する。

user=> (take 5 (iterate inc 1 ))
(1 2 3 4 5)
;takeは指定した数だけコレクションのシーケンスを取得する。
;user=> (take 3 [1 2 3 4 5])
;(1 2 3)

cycleはコレクションをとり、その要素を無限に繰り返すシーケンスを作成。

user=> (take 10 (cycle (range 3)))
(0 1 2 0 1 2 0 1 2 0)

interleaveは複数コレクションを各コレクションから1要素ずつ順に

とってきて並べたシーケンスを返す。

user=> (interleave (range 10) [:a :b :c])
(0 :a 1 :b 2 :c)

interposeはinterleaveと似ていて、第一引数に指定したdelimiterで

第二引数のコレクションを区切った文字列を返す。

user=> (apply str (interpose "," [:a :b :c :d :e]))
":a,:b,:c,:d,:e"

apply strはよく使われるため、str-joinという関数が用意されてるとのこと。

user=> (use '[clojure.contrib.str-utils :only (str-join)])
nil
user=> (str-join "," [1 2 3 4])
"1,2,3,4"

各コレクションの型について、任意の引数をとり、その型のコレクションを作成。

user=> (list 1 2 3)
(1 2 3)
user=> (vector "a" "b" "c")
["a" "b" "c"]
user=> (vec (range 3))
[0 1 2]

user=> (hash-set 1 2 3)
#{1 2 3}
user=> (set [1 2 3])
#{1 2 3}

user=> (hash-map :key1 "val1" :key2 "val2")
{:key2 "val2", :key1 "val1"}

ベクタは任意の引数ととるタイプとコレクションを受け取るタイプがある。

hash-setにもコレクションを受け取るsetという似た関数がある。


次はシーケンスのフィルタ

Connection: close