「はじめてのClojure」の読書メモ (その1)

こないだ参加したClojure夜会 vol.1 で入手した、

はじめてのClojure (I・O BOOKS)

はじめてのClojure (I・O BOOKS)

についての読書メモ。

本の中のコードはnyampass/BeginningClojureで公開されている。 また、本についての正誤表もここで配布されているので事前に手元に持ってきておいたほうが良い。

なお、開発はWin7(64bit)上のLightTable0.6.7を使った。

2章

  • clojureのコメントに3種類の記法があるが、式として完全でないとだめなタイプのコメントにはどのような使い道があるのだろうか。
 (comment (+ 1 2))
 #_(+ 1 2)

clojure-style-guideのここにちょっとだけ利用例があったがいまいちわからん。特定のフォームをコメントアウトする際に使うとのことだが特定のフォームって何?

4章

いきなり本では初見の関数/スペシャルフォームを含むソースコードが出てきたので備忘メモ。

(defn char-range [[first-char end-char]]
  (map char (range (int first-char) (int end-char))))

与えられたchar文字2文字の文字コード間を含むcharのリストを返す関数。

(def key-characters (apply concat (map char-range [[\0 \9] [\a \z] [\A \Z]])))

key-charactersは数字とアルファベットを全て含むcharのリスト。 (map char-range ..) が 3つのリスト(数字、小文字alpha、大文字alpha)を返し、それをconcatでリスト連結している。

(defn index->key [index]
  (loop [index index acc []]
    (if (zero? index)
      (-> acc reverse join)
    (recur (long (/ index (count key-characters)))
      (conj acc (nth key-characters
        (mod index (count key-characters))))))))

index(数字)からkey(文字列)を取得する関数。clojureではこの手の射影関数名に -> をつける慣習がある。

基本的に数字がでかくなると長い文字列を返す。たぶん、key-characters全てを数値とみなした59進数に変換しているような感じ。 urlを登録するたびに払い出す短縮URLを順番に増えていくcurrent-id(index)を59進数であらわした文字列で表現するというわけだ。

(この本では途中までは index = current-id という名前で話が進んでいたのだが急にindexに一本化された。)

  • char

    • charに変換する関数
  • apply

    • 1引数の関数をリストに対して適用する関数。
    user=> (apply + '(1 2 3 4 5)) ;リストの先頭に引数の関数を挿入するイメージ。
    15
    user=> (apply + 1 2  '(1 2 3 4 5)) ;関数の引数を渡すこともできる。
    18
  • loop

    • let句のようにbindingを指定してbodyを実行する。
    • ただし、最後の recur で自loopを再帰的に呼び出すようなイメージでループ処理を書ける。

      • recur に渡す引数は最初のloopのbindingと同じ個数になる。
    • loopは極力使わずにリストへのmapや->の方が関数型プログラム感があって個人的にお洒落には感じる。複雑な条件でリストから実行対象を取捨選択したい場合は使わざるを得ないのだと思うが。

  • conj

    • 第1引数のコレクションに第2引数以降の引数を要素として追加する。
    user=> (conj [1 2] 3)
    [1 2 3]
    user=> (conj [1 2] [3 4])
    [1 2 [3 4]]
    user=> (conj '(1 2 3) 1)
    (1 1 2 3)
    user=> (conj '(1 2 3) 4)
    (4 1 2 3)
    user=> (conj '(1 2 3) 4 5)
    (5 4 1 2 3)
  • ->

    • 第一引数をリスト化して後続の関数の第一引数として順番に渡していく。
    • 入れ子構造をストリームにかけるので鬼便利。
    user=> (-> [1 2 3] (conj 4) (conj 5))
    [1 2 3 4 5]
    
    ;もし -> を使わないと以下のようになって括弧の一番内側から
    ;読み解かないといけない。
    user=>(conj
            (conj [1 2 3] 4)
           5)

なお、instarepl中で (doc 関数名) とやると説明がでる。 (ただし、LightTableは若干コンソール出力が小さいので読みにくいかったのでローカル環境で実行した lein repl で見たほうが良いかも。)