Clojure の基本的なデータ構造体について。
リスト
順序つきコレクション。初期化にはlist
か、'
(クォート) 表記で遅延評価させるかが使える。
user=> (list "B" "M" "W") ("B" "M" "W") user=> '("B" "M" "W") ("B" "M" "W")
クォート1つ外すと関数と評価されてしまい、エラーになる。
user=> ("B" "M" "W") ClassCastException java.lang.String cannot be cast to clojure.lang.IFn user/eval1354 (form-init4901515246432774526.clj:1)
個人的に Lisp っぽさの1つだと考えていた car
や cdr
は存在しない。first
や rest
を使う。cons
はある。
user=> (first '("B" "M" "W")) "B" user=> (rest '("B" "M" "W")) ("M" "W") user=> (cons "B" '("M" "W")) ("B" "M" "W")
ベクタ
順序つきコレクション。ランダムアクセスに最適化されている。角括弧(かくかっこ)[]
で初期化する。
user=> [:java :python :ruby] [:java :python :ruby] user=> (first [:java :python :ruby]) :java user=> (rest [:java :python :ruby]) (:python :ruby) user=> ([:java :python :ruby] 2) :ruby
セット
同じく要素のコレクションだが順序はない。#{}
で初期化する。
user=> #{1 2 3 4 5} #{1 4 3 2 5} ;; 内部では順序を持っているようだが実装依存なのであてにしない
重複は許されない。
user=> #{1 2 3 4 5 1} IllegalArgumentException Duplicate key: 1 clojure.lang.PersistentHashSet.createWithCheck (PersistentHashSet.java:68)
マップ
中括弧(または波括弧){}
で key / value のペアを表現するのがマップ。
user=> {:jimmy "B Rabbit", :bob "Chedar"} {:jimmy "B Rabbit", :bob "Chedar"}
ペア間のカンマはなくても動作するが少し読みにくくなる。
Clojure ではマップもキーワードも関数なので、以下のようにしたい value を取り出すことができる。
user=> ({:jimmy "B Rabbit", :bob "Chedar"} :bob) "Chedar" user=> (:bob {:jimmy "B Rabbit", :bob "Chedar"}) "Chedar"
リストとベクタの使い分け
すべてのClojureシンボルは評価すると何らかの値になります。関数を評価すると「関数の実行結果」という別の値になる。数値はそのまま数値になるように、ベクタは評価するとベクタになります。しかしリストは、評価すると関数として実行されます。エスケープすればリストとして扱えますけど、ただそのまま書くと、第1要素が関数を指すシンボルであり、第2引数以降は関数へ渡す引数だと仮定して評価されます。なので、プログラム的にいわゆるリスト構造の欲しければ、必然的に、ベクタを多用することになります。
Clojureはじめました - tyano's Techlogより
感想
- 括弧が使い分けられていてだいぶすっきりしている。
- Lisp だけあって List Processing するビルトイン関数が充実している
- リストやマップもまた関数として評価されるのが新鮮で面白い。このようにプログラムとデータが同じ形をしているのがなぜ嬉しいのか?…はまだよく理解していないので今度