第2章 Clojure ひとめぐり

2.1 フォーム

何がリテラルになるのかについての説明。有理数型が用意されていることなどCLとの重複は多い。
文字列はCLだと文字のベクタだったが、ClojureではJavaの文字列型を使っている。

;; Common Lisp
(vectorp "hoge") ; T
;; Clojure
(vector? "hoge") ; false

BigDecimal(任意精度の浮動小数点)とBigInt(任意精度の整数演算)もJavaの数値型をそのまま使う。リテラル表記はBigDecimalにはM、BigIntにはNを付ける。

(+ 1 (/ 0.00001M 1000000000000000000))
=> 1.00000000000000000000001M

(* 1000N 1000 1000 1000 1000 1000 1000)
=> 1000000000000000000000N

空リストが真。条件部が偽になるのはnilとfalseだけで、残りは全部真になる。

(if '() "True" "False") ; "True"

2.5 Javaを呼び出す

Javaインスタンスを作るにはnewを使う。

(def rnd (new java.util.Random)) ; => #'user/rnd

メソッド呼出しやフィールドへのアクセスは全部.(ドット)

;; 引数なしのメソッド呼出し
(. rnd nextInt) ; => -1926271163
;; 引数付きのメソッド呼出し (乱数の範囲を指定する)
(. rnd nextInt 10) ; => 9
;; フィールドへのアクセスも似たように書ける
(. Math PI) ; => 3.141592653589793

毎回java.util.Randomと入力するのが面倒なら、useのようにimportを使うことで単にRandomと書ける。

(import '(java.util Random))
(def rnd2 (new Random)) ; => #'user/rnd2

2.6 フロー制御

CLのprognやSchemeのbeginに相当するのは do。CLのdoマクロとは全然違うので注意。

loop-recur構文はSchemeのnamed-letにちょっと似てる。

;; Clojure
(defn my-reverse [lst]
  (loop [lst lst
         product '()]
    (if (empty? lst)
      product
      (recur (rest lst)
             (cons (first lst) product)))))

;; Scheme
(define (my-reverse lst)
  (let loop ((lst lst)
             (product '()))
       (if (null? lst)
         product
         (loop (cdr lst)
           (cons (car lst) product)))))

ClojureのletはCL/Schemeでいうところのlet*と同等。局所変数束縛のところの括弧が少なくなってる。これだとちょっと見にくいのでカンマを入れたりする。Clojureではカンマは空白と同じらしい。じゃあ逆クオートに対するアンクオートはどうなるのかというと~(チルダ)を使う。

;; CL/Scheme
(let* ((i 0)
       (j (+ i 1)))
  (print i)
  (print j))

;; Clojure
(let [i 0 ,
      j (+ i 1)]
  (println i)
  (println j))

condもletと同じように括弧が少ないこと以外は同じ。デフォルト節の条件部に入れるものは真になるものならなんでもいい。今回は:elseとしておく。

(defn fizzbuzz [n]
  (loop [i 1]
    (if (<= i n)
      (do
        (cond (zero? (mod i 15)) (println "Fizz Buzz")
              (zero? (mod i 3)) (println "Fizz")
              (zero? (mod i 5)) (println "Buzz")
              :else (println i))
        (recur (+ i 1))))))