さめたコーヒー

kbaba1001's blog.

プログラミング Clojure 第2版を 1/3 くらい読んだ

プログラミングClojure 第2版

プログラミングClojure 第2版

前回に引き続き Clojure を勉強中。

do

if の中などで戻り値とは別の式を評価したいときに使う。これは結局、副作用を導入することになる。

(defn is-small? [number]
  (if (< number 100)
    "yes"
    (do
       (println "Saw a big number" number)
       "no")))

when は上記を省略してかける。

loop と recur

looprecur による再帰recur により再帰のパフォーマンスを向上できる (末尾最適化を簡易にしたもの)

(loop [result [] x 5]
  (if (zero? x)
    result
    (recur (conj result x) (dec x))))

recurloop の冒頭にジャンプする。 recur の引数は loop の引数に渡されて再帰呼び出しされる。 普通に再帰をするより recur を使って再起したほうが最適化されてメモリーを上手に使える。

関数の中であれば loop は省略できる。その場合、recur は関数の冒頭にジャンプする。

(defn countdown [result x]
  (if (zero? x)
    result
    (recur (conj result x) (dec x))))

for

リストの内包表記。Haskell でいう所の [x | x <- [1..10]] 。 これを clojure で書くと (for [x (range 1 10)] x) になる。

メタデータによる型情報の定義

メタデータで関数の引数と戻り値の型を指定できる。

(defn ^String shout [^String s] (.toUpperCase s))

(shout "abcde")   ;=> "ABCDE"
(shout 12345)     ;=> ClassCastException java.lang.Long cannot be cast to java.lang.String  user/shout

この場合、キャストが出来なければエラーになる。逆にいうと暗黙的なキャストはしているということか?

関数のメタデータには、型情報、メソッドのドキュメント、事前・事後条件がかけるので、結構使い勝って良さそう。

シーケンス

何かしらのコレクションのこと。

文字列やディレクトリ構造、I/Oストリームもシーケンスとして扱える。 シーケンスとして扱えることを seq-able (「シーカブル」)という。 Ruby の Enumerable のように同じインタフェースで操作できるメソッドがある。 map とか reduce とかが使える。 Javaのコレクションもシーケンスとして扱う。

進捗

10章まであるうち3章まで読んだ。