valid,invalid

関心を持てる事柄について

Clojure のデータ構造まとめ

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つだと考えていた carcdr は存在しない。firstrest を使う。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 するビルトイン関数が充実している
  • リストやマップもまた関数として評価されるのが新鮮で面白い。このようにプログラムとデータが同じ形をしているのがなぜ嬉しいのか?…はまだよく理解していないので今度