Clojureコードペット

前置き

Clojureの組み込み関数の使用例を示します。

各使用例の期待値を示すのに、テスティングフレームワークMidjeを使っています。Midjeに関しては、ここ(Part1)ここ(Part2)で説明していますが、まぁ、「 => の右辺が期待値」程度の理解でだいたい読めると思います。

注意
  • この記事は書きかけです
  • 全関数を網羅できるかどうか、心許ないです

トピック一覧

  • 数値演算
  • 論理演算
  • いろいろ比較
  • いろいろキャスト
  • 述語いろいろ(一部)
  • 文字/文字列
  • キーワード/シンボル
  • 関数
  • 関数適用
  • 関数から別の関数を作る
  • 条件分岐
  • ループ
  • メタデータ
  • コレクション
  • リスト
  • ベクタ
  • マップ
  • セット
  • シーケンスの基礎
  • シーケンスから取り出す
  • シーケンスあれこれ
  • 遅延シーケンス
  • シーケンスの述語
  • 検索、正規表現
  • 名前空間
  • 外部Libの制御
  • 標準入出力とファイルI/O
  • Struct/Proxy/Protocol
  • Java Interop
  • References(mutableなデータ)
  • Var
  • Ref
  • Atom
  • Agent
  • Macro
  • Multimethod
  • 並列、並行、非同期処理
  • エラー/例外処理
  • Transientなコレクション
  • グローバル変数
  • ビット操作
  • その他

数値演算

(fact "数値演算"
  (+ 1 2) => 3
  (- 5 2) => 3
  (- 5) => -5           ; 符号反転。
  (* 4 8) => 32
  (/ 3 2) => 3/2        ; 分数(有理数)をサポート。分数には(小数のような)誤差が無い。

  (quot 7 3) => 2       ; 整数除算。
  (rem 7 3) => 1        ; 剰余。
  (mod 7 3) => 1
  (rem -7 3) => -1
  (mod -7 3) => 2       ; (mod n d)は、n以下で最大のdの倍数をいくつオーバーしたか。
  (inc 8) => 9          ; ++
  (dec 9) => 8          ; --
  (min 1 2 3) => 1
  (max -8 -7 -6) => -6
  (max-key count "hello" "foo" "fizbuz") => "fizbuz"
  (min-key #(Math/abs %) -3 1 4) => 1
  (numerator 3/2) => 3       ; 分子。
  (denominator 3/2) => 2     ; 分母。

  (rand-int 10) => (fn [n] (and (>= n 0) (< n 10)))
  (rand 10.0) => (fn [n] (and (>= n 0.0) (< n 10.0)))
  (rand) => (fn [n] (and (>= n 0.0) (< n 1.0)))

  ;; 精度を自動調整する演算。
  (+ Long/MAX_VALUE 1) => (throws ArithmeticException)   ; 普通はoverflowする。
  (+' Long/MAX_VALUE 1) => 9223372036854775808N          ; 自動精度調整。
  (-' Long/MIN_VALUE 1) => -9223372036854775809N
  (*' Long/MAX_VALUE Long/MAX_VALUE) => 85070591730234615847396907784232501249N
  (inc' Long/MAX_VALUE) => 9223372036854775808N
  (dec' Long/MIN_VALUE) => -9223372036854775809N
  (with-precision 10 (/ 1M 3)) => 0.3333333333M                      ; 精度を明示。
  (with-precision 10 :rounding CEILING (/ 1M 3)) => 0.3333333334M    ; 丸め方も明示。

  ;;; プリミティブ値を使った演算(例えばLongよりlong)。
  ;;; overflowも気にしない。

  (unchecked-add 1 2) => 3        ; long
  (unchecked-add-int 1 2) => 3    ; int
  (unchecked-subtract 3 2) => 1
  (unchecked-subtract-int 3 2) => 1
  (unchecked-negate 5) => -5
  (unchecked-negate-int 5) => -5
  (unchecked-multiply 3 5) => 15
  (unchecked-multiply-int 3 5) => 15
  (unchecked-divide-int 5 2) => 2
  (unchecked-remainder-int 5 2) => 1

  (unchecked-inc 2) => 3
  (unchecked-inc-int 2) => 3
  (unchecked-dec 3) => 2
  (unchecked-dec-int 3) => 2

  (unchecked-byte 300) => 44
  (unchecked-char \a) => \a
  (unchecked-int Long/MAX_VALUE) => -1
  (unchecked-short Integer/MAX_VALUE) => -1
  (unchecked-long 0) => 0
  (unchecked-float 0.0) => 0.0
  (unchecked-double 0.0) => 0.0
)

論理演算

(fact "論理演算"
  ;; 論理積。
  ;; 短絡あり(どれか1つでも偽になったら、残りは評価しない)。
  (and 1 2 nil) => nil       ; 最初に偽になった値。
  (and 1 2 false) => false
  (and 1 2 3) => 3           ; 全部が真なら最後の値。

  ;; 論理和。
  ;; 短絡あり(どれか1つでも真になったら、残りは評価しない)。
  (or nil false 3) => 3      ; 最初に真になった値。
  (or nil false nil) => nil  ; 全部が偽なら最後の値。

  ;; 論理否定。
  (not true) => false
  (not nil) => true
)

いろいろ比較

(fact "いろいろ比較"
  ;;; 等価テスト。

  (= "hello!" (str "hello" "!")) => true    ; 内容が同じならOK。
  (= 3/2 6/4 1.5) => false                  ; 型が違うとNG。
  (= (first '()) nil) => true
  (= '(1 2 3) [1 2 3]) => true              ; 要素が同じならOK。
  (= '(1 2 3) #{1 2 3}) => false            ; セットの場合は話しが別。

  (== 3/2 6/4 1.5) => true                  ; ==は数値専用。

  (identical? "hello!" (str "hello" "!")) => false    ; 別オブジェクトはNG。
  (identical? 3/2 6/4) => false
  (identical? (first '()) nil) => true      ; nilはシングルトン。
  (identical? true true) => true
  (identical? false false) => true

  ;;; 不等価テスト。

  (not= "hello!" (str "hello" "!")) => false     ; not=は、=の逆。
  (not= 3/2 6/4 1.5) => true
  (not= (first '()) nil) => false
  (not= '(1 2 3) #{1 2 3}) => true

  ;;; 大小比較。

  (> 3 1) => true
  (>= 3 1) => true
  (< 3 10) => true
  (<= 3 10) => true

  (compare 3 1) => pos?                ; 正の数を返す。
  (compare 3 10) => neg?               ; 負の数を返す。
  (compare 3 3) => zero?               ; 0を返す。
  (compare "xyz" "abc") => pos?
  (compare "abc" "xyz") => neg?
  (compare "abc" "abc") => zero?
  (compare [0 0 0 0] [1 2 3]) => pos?  ; 要素数が多い。
  (compare [0 0] [1 2 3]) => neg?      ; 要素数が少ない。
  (compare [3 3 3] [1 2 3]) => pos?    ; 要素数が同じ。要素ごとの比較。
  (compare [1 1 3] [1 2 3]) => neg?
  (compare [1 2 3] [1 2 3]) => zero?
)

いろいろキャスト

(fact "いろいろキャスト"
  (byte 1.0) => 1
  (short 1.0) => 1
  (long 1.0) => 1
  (float 3/2) => 1.5
  (double 3/2) => 1.5
  (rationalize 1.5) => 3/2             ; 小数から分数へ。
  (char 97) => \a
  (int \space) => 32
  (int \漢) => 28450
  (bigdec 1) => 1M                     ; java.math/BigDecimal
  (bigint 1) => 1N                     ; clojure.lang/BigInt
  (biginteger 1M) => 1                 ; java.math/BigInteger
  (boolean 1) => true?
  (boolean nil) => false?
  (keyword "key") => :key
  (keyword "clojure.core" "key") => :clojure.core/key
  (symbol "name") => 'name
  (symbol "clojure.core" "sym") => 'clojure.core/sym

  (vec '(1 2 3)) => [1 2 3]
  (set [1 2 3 1 2 3]) => #{1 2 3}

  ;; シーケンス(ISeqインターフェイスを実装したもの)へキャスト。
  (seq '(1 2 3)) => '(1 2 3)
  (seq [1 2 3]) => '(1 2 3)
  (seq {:a 1 :b 2 :c 3}) => (just '([:a 1] [:b 2] [:c 3]) :in-any-order)
                                       ; マップをseqするとき順序は予想できない。
                                       ; 後述のsorted-mapを使えば別。
)

述語いろいろ(一部)

(fact "述語いろいろ(一部)"
  ;;; 述語には、trueかfalseを返すものと、任意の真値かnilを返すものがある。
  ;;; 前者の名前は、?で終わる。
  ;;; 後者は、厳密には述語とは言わないかもしれない。

  ;; 数値の性質。
  (number? 1) => true
  (number? 1/2) => true
  (number? 0.1) => true
  (zero? 0) => true
  (pos? 1) => true           ; positive.
  (neg? -1) => true          ; negative.
  (even? 8) => true
  (odd? 9) => true
  (decimal? 1M) => true
  (decimal? 1) => false
  (integer? 1.0) => false
  (float? 1.0) => true
  (ratio? 3/2) => true       ; 分数か?
  (ratio? 1) => false
  (ratio? 1.0) => false
  (rational? 3/2) => true    ; 有理数か?
  (rational? 1) => true
  (rational? 1.0) => false   ; floatには誤差があるからかな。

  ;; 文字/文字列。
  (char? \a) => true
  (char? "hello") => false
  (string? \a) => false
  (string? :key) => false
  (string? "hello") => true

  ;; キーワード。
  (keyword? :key) => true
  (keyword? "key") => false
  (keyword? 'key) => false

  ;; シンボル。
  (symbol? :sym) => false
  (symbol? "sym") => false
  (symbol? 'sym) => true

  ;; nilとブール値。
  (nil? nil) => true
  (nil? '()) => false
  (true? true) => true
  (true? 1) => false
  (false? false) => true
  (false? nil) => false

  ;; コレクション。
  (list? '()) => true
  (vector? []) => true
  (map? {}) => true
  (set? #{}) => true
  (coll? '()) => true             ; みんなコレクション。
  (coll? []) => true
  (coll? {}) => true
  (coll? #{}) => true

  ;; seq?とsequential?は似て非なるものだが、
  ;; 使い道、使い分け共にナゾ。
  (seq? '()) => true              ; リストのみがISeqインターフェイスを実装。
  (seq? []) => false
  (seq? {}) => false
  (seq? #{}) => false
  (seq? "hello") => false
  (sequential? '()) => true       ; リストとベクタのみがSequentialインターフェイスを実装。
  (sequential? []) => true
  (sequential? {}) => false
  (sequential? #{}) => false
  (sequential? "hello") => false
                                  ; いずれにしろ、seqすれば、seq?かつsequential?になる。
                                  ; every-predについては後述。
  ((every-pred seq? sequential?) (seq [1 2 3])) => true
  ((every-pred seq? sequential?) (seq {:a 1 :b 2})) => true
  ((every-pred seq? sequential?) (seq #{1 2 3})) => true
  ((every-pred seq? sequential?) (seq "hello")) => true

  ;; associative?とreversible?もよう分からん。
  ;; associative?なら、assocやget-inが使えるみたい。
  (associative? '()) => false     ; Associativeインターフェイスを実装してる?
  (associative? []) => true
  (associative? {}) => true
  (associative? #{}) => false
  (reversible? '()) => false      ; Reversibleインターフェイスを実装してる?
  (reversible? []) => true
  (reversible? {}) => false
  (reversible? #{}) => false

  ;; Javaオブジェクト。
  (instance? String "Hello") => true
  (instance? Long 1) => true

  (distinct? 1 2 3) => true       ; 一意か。
  (distinct? 1 1 1) => false
  (distinct? 1 :key 'name []) => true
)

文字/文字列

(fact "文字/文字列"
  ;; 文字には、\spaceや\newlineのように、名前を持ったものがある。
  (char 48) => \0
  (char 0x61) => \a
  (char 32) => \space                       ; 半角スペース。
  (char 0x6f22) => \漢                      ; Unicodeのコードポイントで指定。
  (char 0x5b57) => \字
  (char-name-string \newline) => "newline"  ; 改行。
  (char-escape-string \newline) => "\\n"    ; 

  (str \space) => " "
  (str 1 " " 2/3 " " true) => "1 2/3 true"  ; 文字列化して連結。
  (format "%03d" 12) => "012"
  (count "hello") => 5
  (count "全角文字列") => 5                 ; 文字数。
  (count "\ud867\ude3d") => 2               ; 厳密には符号単位の数。←は実は1文字。
  (count (str (char 0xd867) (char 0xde3d))) => 2
  (subs "hello, world" 2 5) => "llo"        ; インデックス2~4。
  (subs "hello, world" 5) => ", world"      ; インデックス5以降。
)

キーワード/シンボル

(fact "キーワード/シンボル"
  {:some-key "foo"}
  (find-keyword "some-key") => :some-key    ; 使用中のキーワードを探す。未使用ならnilを返す。

  ;; ユニークなシンボルを生成(主にマクロ用)。
  (symbol? (gensym)) => true                ; G__1218のような、ユニークなシンボル。
  (subs (str (gensym "foo")) 0 3) => "foo"  ; foo1221とか。
  (= (gensym) (gensym)) => false            ; 必ずユニーク。
  (= (gensym) 'G__1218) => false            ; 偶然一致することも無い。
)

関数

(fact "関数"
  (fn? (fn [x] x)) => true        ; fnで無名関数を作る。
  (fn? (fn [x & rest] x)) => true ; restは、第2引数以降をリストにしたもの。
  (fn? #(%)) => true              ; シュガー。%は引数。
  (fn? #(%1 %2 %3)) => true       ; 複数の引数。

  (defn f [x] x)                  ; 名前付き関数。
  (defn- p [x] x)                 ; privateな名前付き関数。
  (fn? f) => true
  (fn? p) => true
  (definline g [x] x)             ; inline関数。
  (fn? g) => true

  ;; 特殊フォームやマクロがfn?かどうか調べるのは無理。
  ;; コンパイルエラーになるのでMidjeでも捕まえることができない。
;  (fn? if) => java.lang.RuntimeException: Unable to resolve symbol
;  (fn? when) => java.lang.RuntimeException: Can't take value of a macro
  ;; シンボルが特殊フォームかどうかを調べることは可能。
  (special-symbol? 'if) => true
  (special-symbol? 'when) => false

  (ifn? '(1 2 3)) => false        ; 関数的?
  (ifn? [1 2 3]) => true          ; データにも、関数的に使えるものがある。
  (ifn? {:a 1 :b 2}) => true
  (ifn? #{1 2 3}) => true
  (ifn? :key) => true
  (ifn? 'sym) => true
  (ifn? 1) => false

  ((constantly "hello")) => "hello"              ; どんな引数で呼んでも
  ((constantly "hello") 1 2 3) => "hello"        ; "hello"を返す関数を作る。
)

関数適用

(fact "関数適用"
  (apply str '(1 - 3)) => "1-3"   ; 関数適用。
  (apply str 1 '(- 3)) => "1-3"
  (apply str 1 '- '(3)) => "1-3"

  ; ->は、複数のフォームを連鎖させる。
  ; (-> x form1 form2 form3)は、xをform1の第1引数とし、その結果を
  ; form2の第1引数とし、その結果をform3の第1引数とし、その結果を返す。
  (-> {:a [1 2] :b [3 4] :c [5 6]} (get :a) (second)) => 2
  (-> {:html {:head {:title "hello"} :body nil}} :html :head :title) => "hello"
  (-> 3 (+ 2) (- 6) (/ 4)) => -1/4     ; (/ (- (+ 3 2) 6) 4)

  ; ->>は、->と似ているが、第1引数ではなく最後の引数に連鎖させる。
  (->> 3 (+ 2) (- 6) (/ 4)) => 4       ; (/ 4 (- 6 (+ 2 3)))

  ; ->の中で、一時的に->>を使うこともできる。
  ; ->>の中の->はダメ。
  (-> [1 2 3]
      reverse
      (nthrest 1)
      (->> (map inc))
      sort)

  ; as->は、変数のバインドに連鎖させる。
  ; (as-> expr name form1 form2 form3)なら、nameの初期値をexprとしてform1を評価し、
  ; その結果をnameの新しい値としてform2を評価し、
  ; その結果をnameの新しい値としてform3を評価し、
  ; その結果を返す。
  (as-> 0 x (+ x 2) (* x x) (/ 5 x)) => 5/4

  ; cond->は、条件を満たすフォームに対してのみ、->的な連鎖を行う。
  ; (cond-> x test1 form1 test2 form2 test3 form3)は、
  ; testが真なformだけを抜粋して、(-> x form...)する。
  (let [x 3]
    (cond-> x (even? x) (+ 2) (odd? x) (- 6) (pos? x) (/ 4)) => -3/4)

  ; cond->>は、cond->と似ているが、->>的に連鎖させる点が違う。
  (let [x 3]
    (cond->> x (even? x) (+ 2) (odd? x) (- 6) (pos? x) (/ 4)) => 4/3)

  ; some->は、->と似ているが、途中の結果がnilになったら連鎖を止める。
  (some-> {:a [1 2] :b [3 4] :c [5 6]} (get :a) (second)) => 2
  (some-> {:a [1 2] :b [3 4] :c [5 6]} (get :d) (second)) => nil

  ; some->>は、some->と似ているが、->>的に連鎖させる点が違う。
  (some->> 3 (+ 2) #{1 2 3 4 5} (/ 4)) => 4/5
  (some->> 3 (+ 2) #{1 2 3 4} (/ 4)) => nil
)

関数から別の関数を作る

(fact "関数から別の関数を作る"
  ;; memfnは、Javaのメソッドを関数化する。
  (first (map (memfn getClass) '("Hello" 1))) => java.lang.String

  ;; compは、関数を合成した関数を作る。
  ((comp zero? dec) 1) => true         ; decしてzero?する関数。

  ;; complementは、補完関数を作る。論理否定の効果。
  ((complement zero?) 0) => false

  ;; partialは、部分適用した関数を作る。
  ((partial map inc) [1 2 3]) => [2 3 4]

  ;; juxtは、併置(juxtaposition)する関数を作る。
  ;; (juxt f g h)は、引数へ関数f、g、hを適用した結果をリストで返すような関数を作る。
  ((juxt + *) 1 2 3 4) => '(10 24)     ; +と*を'(1 2 3 4)へ適用すると、10と24になる。
  ((juxt :a :b) {:a 1 :b 2 :c 3}) => '(1 2)
                                       ; マップに対してのキーワードは、関数に相当。

  ;; memoizeは、メモ化。答えをキャッシュする関数を作る。
  ((memoize #(* % %)) 3) => 9
  ((memoize #(* % %)) 3) => 9          ; 2度目は、*を呼ばない。

  ;; fnilは、引数がnilのときにデフォルト値を使うような関数を作る。
  ((fnil #(str % "!") "empty") "ok") => "ok!"
  ((fnil #(str % "!") "empty") nil) => "empty!"  ; デフォルト値を使用。
  ((fnil #(str %1 %2 "!") "para1" "para2") 1 nil) => "1para2!"

  ;; every-predは、複数の関数の結果をevery?する関数を作る。
  ;; (every-pred f g h)が作る関数は…
  ;;   ・引数にf、g、hを順に適用する
  ;;   ・ただし、途中で結果が偽になったら、falseを返して終わる
  ;;   ・全部が真ならtrueを返す
  ((every-pred number? odd? #(> % 10)) 3) => false

  ;; some-fnは、複数の関数の結果をsomeする関数を作る。
  ;; (some-fn f g h)が作る関数は…
  ;;   ・引数にf、g、hを順に適用する
  ;;   ・ただし、途中で結果が真になったら、それを返して終わる
  ;;   ・全部が偽なら、最後の結果(falseかnil)を返す
  ((some-fn second #(nth % 2)) '(1 nil 3)) => 3
)

条件分岐

(fact "条件分岐"
  (if (even? 2) 1 2) => 1    ; 2分岐。thenとelseに1つずつ式を書ける。
  (if (even? 3) 1 2) => 2

  (if-not (even? 2) 1 2) => 2
  (if-not (even? 3) 1 2) => 1

  (if-let [x (nth '(1 2 3) 2 nil)] 'foo 'bar) => 'foo  ; letした値で分岐。
  (if-let [x (nth '(1 2 3) 3 nil)] 'foo 'bar) => 'bar

  (when (#{1 2 3} 1) 1 2 3) => 3       ; whenはifのthen専用版。複数の式が書ける。
  (when (#{1 2 3} 4) 1 2 3) => nil
  (when-not (#{1 2 3} 4) 1 2 3) => 3   ; unlessやね。

  (when-let [x (first '(1))] 1 2 3) => 3    ; if-letのwhenバージョン。
  (when-let [x (second '(1))] 1 2 3) => nil

  (when-first [x '(1)] 1 2 3) => 3     ; seqして、そのfirstに対してwhen-letする。
  (when-first [x '()] 1 2 3) => nil
  (when-first [x [1 2 3]] x) => 1

  (let [x 3]
    (cond                         ; 多分岐。
      (pos? x) "pos"
      (neg? x) "neg"
      :else "zero")) => "pos"

  (case (compare 3 0)             ; 多分岐。第1引数がexpr。
    1 "pos"                       ; exprが1なら"pos"。
    -1 "neg"                      ; exprが-1なら"neg"。
    "zero") => "pos"              ; デフォルトは"zero"。
  (case (nth '(1 2 3) 0)
    (0 2 4 6) "even"              ; exprがリスト内のどれかなら"even"。
    (1 3 5 7) "odd") => "odd"     ; 
                                  ; 期待値は静的に決まる値でなければならない。
                                  ; デフォルト指定が無く、どれにもマッチしないなら例外。

  ;; condpも多分岐だが、複雑。
  ;; (condp pred expr
  ;;   test1 expr1
  ;;   test2 expr2)は、
  ;; (pred test1 expr)が真ならexpr1を返し、
  ;; (pred test2 expr)が真ならexpr2を返す。
  ;; (condp pred expr
  ;;   test1 :>> fn1
  ;;   test2 :>> fn2)は、
  ;; (pred test1 expr)が真なら、その値を引数にfn1を呼んで結果を返し、
  ;; (pred test2 expr)が真なら、その値を引数にfn2を呼んで結果を返す。
  ;; :>>は、単なるキーワード。
  ;; :>>の右辺は1引数の関数。
  (let [num 3]
    (condp = num                  ; 多分岐。
      1 "one"
      2 "two"
      3 "three"
      "unexpected")) => "three"
  (condp some '(1 3 5)
    #(> % 5)             :>> identity  ; identityは引数をそのまま返す関数。
    even?                :>> identity  ; someの場合、:>>の左辺が述語だと、意味がない気がする。
    #(if (odd? %) % nil) :>> identity) => 1
                                       ; これなら意味がある。「最初のodd?」を得た。
  (condp some '(1 2 9)
    #{3 4 5} :>> inc
    #{6 7 8} :>> dec
    #{9 0}   :>> #(* % 10)) => 90
)

ループ

(fact "ループ"
  ;; forは、リスト内包で、遅延シーケンスを返す。機能盛り沢山。
  ;; バインド、バインドの修飾、本体式から成る。
  (for [x '(1 2 3)]          ; バインド。リストの各値をxにバインドしながらループする。
    (inc x)) => '(2 3 4)     ; 本体式。
  (for [x '(1 2 3)           ; 複数のバインドがあると、
        y [:a :b]]           ; その直積(全組み合わせ)に対してループ。
    (list x y)) => '((1 :a) (1 :b) (2 :a) (2 :b) (3 :a) (3 :b))
  (for [x (range)
          :while (< x 5)]              ; バインドの修飾(:while)。
    x) => '(0 1 2 3 4)
  (for [x (range)
          :while (< x 3)
        y {:a \a :b \b}                ; バインドの母集合はseqされる。
          :let [v (second y)]]         ; バインドの修飾(:let)。
    (str x v)) => (just '("0a" "0b" "1a" "1b" "2a" "2b") :in-any-order)
  (for [x (range)
          :while (< x 5)
          :when (even? x)              ; バインドの修飾(:when)。
          :let [y (inc x)]]
    y) => '(1 3 5)

  ;; doseqは、forと同じだが、nilを返す。
  ;; 本体式の副作用だけが欲しいときに使う。
  (with-out-str
    (doseq [x (range)
              :while (< x 5)
              :when (even? x)
              :let [y (inc x)]]
      (print y)
      (print "!")
      y)) => "1!3!5!"

  ;; dotimesは、doseq同様、副作用狙いのループ。
  ;; nilを返す。
  ;; バインドできるのは1つの変数のみ。
  (with-out-str
    (dotimes [x 5]
      (print x)
      (print "!"))) => "0!1!2!3!4!"

  ;; whileは、文字通りwhileループ。
  (def a (atom 5))
  (with-out-str
    (while (pos? @a)
      (print @a)
      (print "!")
      (swap! a dec))) => "5!4!3!2!1!"
)

メタデータ

(fact "メタデータ"
  (def ^:key1 ^:key2 ^String ^{:key3 3 :key4 4} s "hello")
                             ; ^:key1は、^{:key1 true}と等価。
                             ; ^Stringは、^{:tag String}と等価。
                             ; これらのメタデータは、値ではなくVarに付く。
  (:key1 (meta #'s)) => true
  (:key2 (meta #'s)) => true
  (:tag (meta #'s)) => java.lang.String
  (:key3 (meta #'s)) => 3
  (:key4 (meta #'s)) => 4
  (empty? (meta s)) => true  ; 値にはメタデータは付いてない。

  ;; with-metaは、メタデータ付きの値を作る。
  (with-meta '(1 2 3) {:key5 5}) => '(1 2 3)
  (with-meta "hello" {:key5 5}) => throws ClassCastException
                             ; 文字列はメタデータを持てない。
  (def lis (with-meta '(1 2 3) {:key5 5}))
  (:key5 (meta lis)) => 5
  (:key5 (meta #'lis)) => nil

  ;; vary-metaは、既存の値のメタデータを変更した新しい値を作る。
  ;; (vary-meta obj f arg1 arg2 arg3)は、
  ;; (with-meta obj (f (meta obj) arg1 arg2 arg3))と等価。
  (vary-meta '(1 2 3) assoc :key6 6) => '(1 2 3)
  (meta (vary-meta '(1 2 3) assoc :key6 6)) => (contains {:key6 6})

  ;; alter-meta!は、Namespace、Var、Ref、Agent、Atomの
  ;; メタデータをアトミックに変更する。
  ;; (alter-meta! target f arg1 arg2)は、
  ;; targetのメタデータを(f (meta target) arg1 arg2)に変更する。
  (def ^String s "hello")
  (alter-meta! #'s #(assoc % :key7 7))
  (meta #'s) => (contains {:key7 7})

  ;; reset-meta!は、alter-meta!と似ているが、
  ;; メタデータをそっくり入れ替える(現在のメタデータは無視)。
  (def ^String s "hello")
  (reset-meta! #'s {:key1 100})
  (meta #'s) => (just {:key1 100})
)

コレクション

(fact "コレクション"
  ;; コレクション用の関数は、文字列に対しても使える場合がある。
  (count '(1 2 3)) => 3
  (count {:a 1 :b 2}) => 2
  (count "hello") => 5

  (empty '(1 2 3)) => '()              ; 同じ型で、空っぽにしたやつ。
  (empty '([1] #{2} {:a 1})) => '()    ; ネストしても無視。

  (conj '(1 2 3) 4) => '(4 1 2 3)      ; 要素の追加。
                                       ; 追加後も、コレクション種別は維持。
  (conj [1 2 3] 4) => [1 2 3 4]        ; ベクタなら最後尾へ。
  (conj #{1 2 3} 4) => #{1 2 3 4}

  (into '() '(1 2 3)) => '(3 2 1)      ; 右の要素を左へ順にconjする。
  (into [1 2 3] '(4 5)) => [1 2 3 4 5]

  (assoc [1 2 3] 0 2) => [2 2 3]            ; 要素の変更。
  (assoc [1 2 3] 3 4) => [1 2 3 4]          ; 追加も可能。
  (assoc {:a 1 :b 2} :a 2) => {:a 2 :b 2}
  (assoc {:a 1 :b 2} :c 3) => {:a 1 :b 2 :c 3}

  ; ネストしたベクタを掘って要素を変更。
  ; ベクタとマップが混在してもOK。
  ; 第2引数のベクタに、検索キーを列挙する。
  ; assoc-inでは、第3引数に新しい値を指定。
  ; update-inでは、第3引数に新しい値を決める関数を指定。
  (assoc-in [[:a :b] [:c :d]] [1 0] :C) => [[:a :b] [:C :d]]
  (assoc-in [{:a 1 :b 2} {:c 1}] [0 :b] 20) => [{:a 1, :b 20} {:c 1}]
  (update-in [[:a :b] [:c :d]] [1 0] str) => [[:a :b] [":c" :d]]
  (update-in [{:a 1 :b 2} {:c 1}] [0 :b] inc) => [{:a 1, :b 3} {:c 1}]

  ; peekとpopは、リストやベクタをスタック的に使うための関数。
  ; peekは、スタックのトップを覗く。
  ; popは、文字通りポップ。
  ; プッシュに相当する関数は、conj。
  ; ベクタに対するpeekは、lastより高速。
  (peek '(1 2 3)) => 1
  (peek [1 2 3]) => 3
  (pop '(1 2 3)) => '(2 3)
  (pop [1 2 3]) => [1 2]
  (pop '()) => throws IllegalStateException
  (pop []) => throws IllegalStateException

  (.indexOf '(1 2 3) 2) => 1           ; java.util.Vector#indexOfを呼ぶ感じ。
  (.indexOf [1 2 3] 2) => 1
  (.indexOf [1 2 3] 4) => -1
  (.lastIndexOf '(1 2 3 2 1) 2) => 3   ; 後ろから検索。
  (.lastIndexOf [1 2 3 2 1] 2) => 3
  (.lastIndexOf [1 2 3 2 1] 4) => -1

  (get [1 2 3] 2) => 3            ; getは、nthに似ているが
  (get [1 2 3] 4) => nil          ; 境界を越えたら例外ではなくnil。
  (get {:a 1 :b 2} :a) => 1       ; マップやセットにも使える。
  (get {:a 1 :b 2} :c) => nil
  (get #{:a :b :c} :a) => :a
  (get #{:a :b :c} :d) => nil

  (get-in [[:a :b] [:c :d]] [1 0]) => :c    ; ネストしたベクタを掘る。
  (get-in [{:a 1 :b 2} {:c 1}] [0 :b]) => 2 ; マップでもOK。

  (find [1 2 3] 0) => [0 1]       ; インデックスをキーに、インデックスと値のペアを得る。
  (find [1 2 3] 2) => [2 3]
  (find [1 2 3] 3) => nil
  (find {:a 1 :b 2} :a) => [:a 1]
  (find {:a 1 :b 2} :c) => nil

  (frequencies [3 1 3 6 7 1 3 7]) => {1 2 3 3 6 1 7 2}     ; 値と頻度のマップ。

  ;; reduce-kvは、reduceと似ているが、(reduce-kv f init coll)は、
  ;;   ・collをseqしない
  ;;   ・initが必須
  ;;   ・fは3引数関数
  ;; collがベクタの場合は、(f (f (f init 0 e1) 1 e2) 2 e3)...を返す。
  ;; collがマップの場合は、(f (f (f init k1 e1) k2 e2) k3 e3)...を返す。
  (reduce-kv #(conj %1 [%2 %3]) [] [:a :b :c]) => [[0 :a] [1 :b] [2 :c]]
  (reduce-kv #(str (if (nil? %1) "" (str %1 ", ")) %2 "=" %3) nil {:a 1 :b 2}) => ":a=1, :b=2"

  ;; shuffleは、文字通りシャッフルし、ベクタで返す。
  (shuffle '(1 2 3)) => (contains [1 2 3] :in-any-order)
  (shuffle [1 2 3]) => (contains [1 2 3] :in-any-order)
  (shuffle {:a 1 :b 2 :c 3}) => throws ClassCastException  ; マップには使えない。
  (shuffle #{1 2 3}) => (contains [1 2 3] :in-any-order)
  (shuffle "hello") => throws ClassCastException           ; 文字列には使えない。

  ;;; 述語。

  ;; 空っぽか?
  ;; シーケンスにも使える。
  (empty? '()) => true
  (empty? "") => true
  (not-empty '()) => nil
  (not-empty '(1 2 3)) => '(1 2 3)
  (not-empty "hello") => "hello"

  (sorted? (sorted-map :c 3 :a 1 :b 2)) => true
  (sorted? (sorted-set 3 1 2)) => true
  (sorted? '(1 2 3)) => false          ; 手でソートしてもダメ。
  (counted? '(1 2 3)) => true          ; 一定時間でcountできるか?
  (counted? "hello") => false

  (contains? [:a :b :c] 0) => true     ; インデックス0に、要素があるか?
  (contains? {:a 1 :b 2} :b) => true   ; キー:bに、要素があるか?
  (contains? #{:a :b :c} :c) => true   ; 要素:cを含むか?
  (contains? "hello" 5) => false        ; インデックス5に、文字があるか?

  ;; every?、not-every?、some、not-any?は、
  ;; 述語とコレクションを引数にとり、コレクションの要素の
  ;; 全てが述語を満たすか、全てが満たさないか、1つでも満たすか、1つも満たさないか
  ;; を調べる。
  ;; someはsome?ではない。真の場合にtrueではなく述語の結果を返すので。
  (every? odd? '(1 3 5)) => true
  (not-every? odd? '(1 3 5 6)) => true
  (some second ['(1) ["hello"] '(:a :b :c)]) => :b
  (not-any? odd? '(2 4 6)) => true
  (every? char? "hello") => true
  (not-every? char? "hello") => false
  (some number? "hello") => nil
  (not-any? string? "hello") => true
)

リスト

(fact "リスト"
  (list 1 2 3) => '(1 2 3)
  (list* 1 2 '(3)) => '(1 2 3)            ; list*なら、最後の引数にコレクションを指定。
  (list* 1 2 [3 4 5]) => '(1 2 3 4 5)
)

ベクタ

(fact "ベクタ"
  (vector 1 2 3) => [1 2 3]
  (vector-of :long 1 2 3) => [1 2 3]      ; 要素の型を限定。Javaのprimitive型を指定可能。
                                          ; 要素がbox化されない(Objectにならない)ので軽い。

  ([1 2 3] 2) => 3           ; ベクタは関数的に使える。(nth [1 2 3] 2)と等価。
  ([1 2 3] 4) => throws IndexOutOfBoundsException

  (subvec [1 2 3 4 5] 1 3) => [2 3]
  (subvec [1 2 3 4 5] 3) => [4 5]

  ;; mapvは、mapと似ているが、シーケンスではなくベクタを返す。
  (mapv inc '(1 2 3)) => [2 3 4]
  (mapv + '(1 2 3) '(4 5 6)) => [5 7 9]

  ;; filtervは、filterと似ているが、シーケンスではなくベクタを返す。
  (filterv odd? '(1 2 3)) => [1 3]
)

マップ

(fact "マップ"
  ;; マップは3種類あるが、sorted-map以外は、順不同と考えた方が良いかも。
  ;; sorted-mapは、キーでソートされる。
  ;; リテラル表記のマップはarray-mapになるが、例外もあるみたい。
  ;; Midjeの => は、マップの順番をチェックしない(内容が同じならOK)。
  (hash-map :a 1 :b 2) => {:a 1 :b 2}       ; 実質、順不同。
  (array-map :a 1 :b 2) => {:a 1 :b 2}      ; 追加順を保つ(追加場所は先頭)。
  (sorted-map :a 1 :b 2) => {:a 1 :b 2}     ; キーでソートされる。
  (seq                                      ; 比較関数を指定。
    (sorted-map-by #(compare %2 %1) :a 1 :b 2)) => (just '([:b 2] [:a 1]))
  (seq (zipmap [:a :b :c] [1 2 3])) => (just '([:c 3] [:b 2] [:a 1]))
                                            ; キーのコレクションと値のコレクションをzip。

  ({:a 1 :b 2 :c 3} :b) => 2      ; マップは関数的に使える。(get {:a 1 :b 2 :c 3} :b)と等価。
  ({:a 1 :b 2 :c 3} :d) => nil
  (:b {:a 1 :b 2 :c 3}) => 2      ; マップに対しては、キーワードも関数的に使える。
  ('c {'a 1 'b 2 'c 3}) => 3      ; シンボルも関数的に使える。

  (keys (sorted-map :a 1 :b 2 :c 3)) => '(:a :b :c)   ; キーのシーケンス。
  (vals (sorted-map :a 1 :b 2 :c 3)) => '(1 2 3)      ; 値のシーケンス。

  (dissoc {:a 1 :b 2 :c 3} :b) => {:a 1 :c 3}    ; エントリを削除。
  (dissoc {:a 1 :b 2 :c 3} :b :c) => {:a 1}

  ;; マージ。
  (merge {:a 1} {:b 2 :c 3}) => {:a 1 :b 2 :c 3}
  (merge {:a 1} {:a 2}) => {:a 2}                     ; キー衝突時は後勝ち。
  (merge-with + {:a 1} {:a 2} {:b 2}) => {:a 3 :b 2}  ; 値のマージ用関数を指定。

  (select-keys {:a 1 :b 2 :c 3} [:a :c]) => {:a 1 :c 3}

  ;; keyとvalは、マップのエントリに対して作用する関数。
  ;; マップのエントリとは、マップへ関数をmapするときに、
  ;; 関数の引数に指定されるもの。
  ;; あるいは、マップをseqした後の各要素。
  ;; [:a 1]のようなベクタではない(似てるけど)。
  (map key (sorted-map :a 1 :b 2 :c 3)) => '(:a :b :c)
  (map val (sorted-map :a 1 :b 2 :c 3)) => '(1 2 3)
  (key (first (sorted-map :a 1 :b 2 :c 3))) => :a
  (val (second (sorted-map :a 1 :b 2 :c 3))) => 2
)

セット

(fact "セット"
  (hash-set :a :b :c :b :a) => #{:a :b :c}
  (seq (sorted-set :a :b :c :b :a)) => (just '(:a :b :c))
  (seq (sorted-set-by > 1 2 3 2 1)) => (just '(3 2 1))

  (#{:a :b :c} :a) => :a          ; セットは関数的に使える。(get #{:a :b :c} :a)と等価。
  (:b #{:a :b :c}) => :b          ; セットに対しては、キーワードも関数的に使える。
  ('c #{'a 'b 'c}) => 'c          ; シンボルも関数的に使える。

  (disj #{:a :b :c} :a) => #{:b :c}
  (disj #{:a :b :c} :a :b) => #{:c}
)

シーケンスの基礎

(fact "シーケンスの基礎"
  ;;; シーケンスとは「何かの並び」を抽象化した概念で、以下の特徴を持つ。
  ;;;   ・1つ以上の要素を持つ
  ;;;   ・先頭要素を取り出せる
  ;;;   ・先頭の「続き」を取り出せる
  ;;;   ・「続き」はシーケンスかnilである
  ;;; ただ、これだけなら明快だが、実は「空っぽのシーケンス」も認められているので
  ;;; 要素が尽きたらnilになるケースと空シーケンスになるケースがある。
  ;;; 関数ごとに、コーナーケースを調べた方が無難。

  ;; リストは、それ自身がシーケンス。
  ;; リスト以外のコレクションや、文字列、Javaの配列などは、seqにより
  ;; シーケンス化することができる。
  (seq [1 2 3]) => '(1 2 3)
  (seq (sorted-map :a 1 :b 2 :c 3)) => '([:a 1] [:b 2] [:c 3])
  (seq #{1 2 3}) => '(1 2 3)
  (seq "hello") => '(\h \e \l \l \o)
  (seq (.split #"[:,]" "a:b,c")) => '("a" "b" "c")
  (seq []) => nil
  (seq "") => nil
  (seq nil) => nil

  ;; コレクションのシーケンス化は、sequenceでも可能。
  ;; 空のコレクションやnilに対する挙動がseqとは異なる。
  (sequence [1 2 3]) => '(1 2 3)
  (sequence "hello") => '(\h \e \l \l \o)
  (sequence []) => '()
  (sequence "") => '()
  (sequence nil) => '()

  ;; 先頭を取り出す。
  ;; シーケンス用の関数の多くは、内部で、引数に指定されたコレクションをseqする。
  (first '(1 2 3)) => 1
  (first (sorted-map :a 1 :b 2 :c 3)) => [:a 1]
  (first "hello") => \h
  (first (.split #"[:,]" "a:b,c")) => "a"
  (first '()) => nil
  (first nil) => nil

  ;; 続きを取り出す。
  (next '(1 2 3)) => '(2 3)
  (next (sorted-map :a 1 :b 2 :c 3)) => '([:b 2] [:c 3])
  (next '[1]) => nil
  (next "") => nil
  (next nil) => nil
)

シーケンスから取り出す

(fact "シーケンスから取り出す"
  ;; secondは、2番目の要素。
  (second [1 2 3]) => 2
  (second (sorted-map :a 1 :b 2 :c 3)) => [:b 2]
  (second "hello") => \e
  (second (.split #"[:,]" "a:b,c")) => "b"
  (second '(1)) => nil

  ;; lastは、最後の要素。
  (last [1 2 3]) => 3
  (last (sorted-map :a 1 :b 2 :c 3)) => [:c 3]
  (last "hello") => \o
  (last (.split #"[:,]" "a:b,c")) => "c"
  (last '()) => nil
  (last nil) => nil

  ;; ffirstは、firstのfirst。
  ;; nfirstは、firstのnext。
  ;; fnextは、nextのfirst。
  ;; nnextは、nextのnext。
  (ffirst [[1 2 3] [4 5 6]]) => 1
  (nfirst [[1 2 3] [4 5 6]]) => [2 3]
  (fnext [[1 2 3] [4 5 6]]) => [4 5 6]
  (nnext [[1 2 3] [4 5 6] 7]) => [7]

  ;; nthは、N番目の要素。
  ;; ただし、なぜかマップには使えない。バグじゃないのかな?
  ;; はみ出たら例外。
  (nth '(1 2 3) 2) => 3
  (nth [1 2 3] 2) => 3
  (nth [1 2 3] 4) => throws IndexOutOfBoundsException
  (nth [] 0) => throws IndexOutOfBoundsException
  (nth (sorted-map :a 1 :b 2 :c 3) 2) => throws UnsupportedOperationException
                                       ; マップには使えない。
  (nth (seq (sorted-map :a 1 :b 2 :c 3)) 0) => [:a 1]
  (nth "hello" 4) => \o
  (nth (.split #"[:,]" "a:b,c") 2) => "c"

  ;; rand-nthは、ランダムに1つ取り出す。
  ;; nthと同様、マップには使えない。
  (rand-nth '(1 2 3)) => (roughly 2 1)      ; 2±1。
  (rand-nth (sorted-map :a 1 :b 2 :c 3)) => throws UnsupportedOperationException
  (rand-nth '()) => throws IndexOutOfBoundsException
  (rand-nth nil) => throws IndexOutOfBoundsException

  ;; nthnextは、nthをN回繰り返す。
  ;; マップにも使える。
  (nthnext '(1 2 3) 0) => '(1 2 3)
  (nthnext '(1 2 3) 1) => '(2 3)
  (nthnext '(1 2 3) 2) => '(3)
  (nthnext '(1 2 3) 3) => nil
  (nthnext '(1 2 3) 4) => nil
  (nthnext (sorted-map :a 1 :b 2 :c 3) 1) => '([:b 2] [:c 3])

  ;; butlastは、nextの反対。最後の要素を除いたシーケンス。
  (butlast '(1 2 3)) => '(1 2)
  (butlast (sorted-map :a 1 :b 2 :c 3)) => '([:a 1] [:b 2])
  (butlast '[1]) => nil
  (butlast "") => nil
  (butlast nil) => nil

  ;; restは、先頭を除いた残り。
  ;; nextと似ているが、nextがnilを返すケースで、restは空シーケンスを返す。
  (rest '(1 2 3)) => '(2 3)
  (rest (sorted-map :a 1 :b 2 :c 3)) => '([:b 2] [:c 3])
  (rest '[1]) => '()
  (rest "") => '()
  (rest nil) => '()

  ;; nthrestは、nthnextのrest版。
  (nthrest '(1 2 3) 0) => '(1 2 3)
  (nthrest '(1 2 3) 1) => '(2 3)
  (nthrest '(1 2 3) 2) => '(3)
  (nthrest '(1 2 3) 3) => '()
  (nthrest '(1 2 3) 4) => '()
  (nthrest (sorted-map :a 1 :b 2 :c 3) 1) => '([:b 2] [:c 3])
)

シーケンスあれこれ

(fact "シーケンスあれこれ"
  ;; consは、コレクションをseqし、要素を追加。
  ;; 追加後は、コレクション種別が失われる。
  (cons 4 '(1 2 3)) => '(4 1 2 3)
  (cons 4 [1 2 3]) => '(4 1 2 3)
  (cons 4 {:a 1 :b 2}) => (contains '(4 [:a 1] [:b 2]) :in-any-order)
  (cons 4 #{1 2 3}) => '(4 1 2 3)

  ;; replaceは、コレクションの要素をテーブルから検索し、検索結果で入れ替える。
  ;; (replace table coll)は、collの要素eについて(table e)が真なら
  ;; 要素eを(table e)と入れ替える。
  ;; 検索テーブルは、ベクタかマップ。
  ;; collがベクタならベクタを返し、ベクタ以外ならシーケンスを返す。
  (replace [:a :b :c] '(0 0 0)) => '(:a :a :a)        ; ([:a :b :c] 0)は:one。
  (replace {:a 1 :b 2} [:a :b :a :c]) => [1 2 1 :c]   ; マッチしなければ入れ替えない。

  ;; rseqは、ベクタかソート済みマップを逆順にしたシーケンスを返す。
  (rseq [1 2 3]) => [3 2 1]
  (rseq (sorted-map :a 1 :b 2 :c 3)) => '([:c 3] [:b 2] [:a 1])

  ;; subseqは、ソート済みのコレクションから、ある範囲の要素を取り出したシーケンスを作る。
  ;; 条件には、<か、<=か、>か、>=を指定可能。
  ; rsubseqは、subseqしてreverseする。
  (subseq (sorted-set 1 5 4 2 3) > 2) => '(3 4 5)     ; 2より大きいもの。
  (subseq (sorted-set 1 5 4 2 3) > 2 <= 4) => '(3 4)  ; 2より大きく、4以下のもの。
  (subseq (sorted-map :a 1 :c 2 :d 3 :b 4) >= :c) => '([:c 2] [:d 3])
  (rsubseq (sorted-set 1 5 4 2 3) > 2) => '(5 4 3)

  ;; split-atは、「先頭のn個」と「それ以降」に分ける。
  ;; つまり、takeとdropの結果からなるベクタを返す。
  ;; split-withは、split-atと似ているが、takeとdropではなく、
  ;; take-whileとdrop-whileの結果からなるベクタを返す。
  (split-at 2 [1 2 3 4 5]) => ['(1 2) '(3 4 5)]
  (split-at 2 {:a 1 :b 2}) => ['([:a 1] [:b 2]) '()]
  (split-at 10 {:a 1 :b 2}) => ['([:a 1] [:b 2]) '()]
  (split-with odd? [1 3 5 4 8 7 9]) => ['(1 3 5) '(4 8 7 9)]

  ;; reduceは、コレクションを折り畳む。
  ;; (reduce f coll)は、(seq coll)の要素e1, e2, e3, ...に対して、
  ;; (f (f (f e1 e2) e3) e4)... を返す。
  ;; (reduce f init coll)は、(f (f (f (f init e1) e2) e3) e4)... を返す。
  (reduce + [1 2 3]) => 6
  (reduce #(+ %1 (second %2)) 0 {:a 1 :b 2 :c 3}) => 6

  ;; reverseは、逆順にする。
  (reverse "hello") => '(\o \l \l \e \h)
  (reverse (sorted-map :a 1 :b 2 :c 3)) => '([:c 3] [:b 2] [:a 1])
  (reverse ['(1 2) '(3 4) '(5 6)]) => '((5 6) (3 4) (1 2))

  ;; sortは、ソートする。
  ;; 比較関数を指定可能。デフォルトはcompare。
  ;; Javaの配列をソートすると、入力された配列が破壊される。
  ;; これを避けるにはacloneなどで複製を作る。
  ;; sort-byは、関数の結果によってソートする。
  ;; Java配列が破壊される点は、sortと同様。
  (sort ["aaa" "bb" "c"]) => '("aaa" "bb" "c")
  (sort > [3 1 2 4]) => '(4 3 2 1)
  (sort-by count ["aaa" "bb" "c"]) => '("c" "bb" "aaa")
  (sort-by first > [[1 2] [2 2] [2 3]]) => '([2 2] [2 3] [1 2])
)

遅延シーケンス

(fact "遅延シーケンス"
  ;;; 遅延シーケンスの要素は、使われるときに初めて評価(決定)される。
  ;;; 遅延シーケンスを作るとき、要素を具体的に指定する必要は無く(指定してもいいけど)、
  ;;; 要素の計算方法を指定すれば良い。

  ;; lazy-seqは、遅延シーケンスを作る。
  (lazy-seq '(1 2 3)) => '(1 2 3)
  (defn fib [a b] (cons a (lazy-seq (fib b (+ a b)))))
  (take 7 (fib 1 1)) => '(1 1 2 3 5 8 13)

  ;; repeatedlyとiterateは、ある関数を呼んだ結果を要素とする無限シーケンスを作る。
  ;; repeatedlyに指定する関数は、引数を持ってはならない。
  ;; iterateに指定する関数は、1引数の関数。
  ;; iterateの引数には、関数fと初期値xを指定するが、返すシーケンスの最初の要素がx、
  ;; 2番目の要素が(f x)、3番目の要素が(f (f x))、と続く。
  (take 3 (repeatedly (constantly true))) => '(true true true)
  (def counter (let [c (atom 8)] #(swap! c inc)))     ; 呼ぶたびに返す値が変わる関数。
  (take 3 (repeatedly counter)) => '(9 10 11)
  (take 3 (iterate inc 3)) => '(3 4 5)
  (take 3 (iterate #(* % %) 3)) => '(3 9 81)

  ;; rangeは、整数の数列を遅延シーケンスで返す。
  (range 3 10) => '(3 4 5 6 7 8 9)          ; 開始値と終了値を指定。終了値は含まない。
  (range 3 3) => '()
  (range 10) => '(0 1 2 3 4 5 6 7 8 9)      ; 開始値のデフォルトは0。
  (range 3 10 2) => '(3 5 7 9)              ; 増加量を指定可能。
  (range 3 -10 -4) => '(3 -1 -5 -9)
  (nth (range) 123) => 123                  ; 引数なしなら、0から始まる無限シーケンス。

  ;; repeatは、ある値を繰り返す。
  (repeat 3 \a) => '(\a \a \a)              ; 繰り返す回数を指定。
  (repeat 0 \a) => '()
  (nth (repeat "!") 123) => "!"             ; 無限に繰り返す。

  ;; cycleは、あるシーケンスを繰り返す。
  (take 10 (cycle '(1 2 3))) => '(1 2 3 1 2 3 1 2 3 1)
  (take 10 (cycle "hello")) => '(\h \e \l \l \o \h \e \l \l \o)

  ;; takeは、先頭からいくつか取り出す。
  ;; take-lastは、うしろから取り出す。
  (take 3 '(1 2 3 4 5)) => '(1 2 3)
  (take 0 '(1 2 3 4 5)) => '()                   ; 0個なら空シーケンス。
  (take 100 '(1 2 3 4 5)) => '(1 2 3 4 5)
  (take-last 3 '(1 2 3 4 5)) => '(3 4 5)
  (take-last 0 '(1 2 3 4 5)) => nil              ; 0個ならnil。バグじゃないのかな?
  (take-last 100 '(1 2 3 4 5)) => '(1 2 3 4 5)

  ;; dropは、先頭からいくつか取り除く。
  ;; drop-lastは、うしろから取り除く。
  (drop 3 '(1 2 3 4 5)) => '(4 5)
  (drop 100 '(1 2 3 4 5)) => '()
  (drop-last 3 '(1 2 3 4 5)) => '(1 2)
  (drop-last 100 '(1 2 3 4 5)) => '()

  ;; take-whileは、先頭から、条件に合わなくなるまで取り出す。
  ;; drop-whileは、先頭から、条件に合わなくなるまで捨てた、残りのシーケンスを返す。
  (take-while even? '(0 2 4 5 6 8 10)) => '(0 2 4)
  (drop-while even? '(0 2 4 5 6 8 10)) => '(5 6 8 10)

  ;; distinctは、重複を取り除く。
  (distinct [1 2 3 2 1]) => '(1 2 3)
  (distinct []) => '()

  ;; filterは、条件に合う要素のみを抽出。
  (filter even? (range 10)) => '(0 2 4 6 8)
  (filter (constantly false) (range 10)) => '()

  ;; removeは、条件に合う要素を除く。
  (remove even? (range 10)) => '(1 3 5 7 9)
  (remove (constantly true) (range 10)) => '()

  ;; take-nthは、一定間隔で抽出。
  (take-nth 3 (range 10)) => '(0 3 6 9)
  (take-nth 3 []) => '()

  ;; concatは、複数のシーケンスを連結。
  ;; lazy-catは、concatと似ているが、各シーケンスをlazy-seqしてから
  ;; 連結する。
  (concat '(1 2 3) '(4 5 6) [7 8 9] ) => '(1 2 3 4 5 6 7 8 9)
  (lazy-cat '(1 2 3) '(4 5 6) [7 8 9] ) => '(1 2 3 4 5 6 7 8 9)

  ;; interleaveは、2つのシーケンスから要素を交互に取り出して1つのシーケンスにする。
  ;; 3つ以上のシーケンスでもOK。
  (interleave [:a :b] [1 2]) => '(:a 1 :b 2)
  (interleave [:a :b] [1 2] [true false]) => '(:a 1 true :b 2 false)

  ;; interposeは、シーケンスの各要素の間にある値を挿入したシーケンスを作る。
  (interpose "," ["red" "green" "blue"]) => '("red" "," "green" "," "blue")

  ;; map系の関数は、関数とシーケンスを引数にとり、
  ;; シーケンスの要素に関数を適用した結果から成る遅延シーケンスを返す。
  ;; mapは、シーケンスの要素に、順に関数を適用する。
  ;; pmapは、関数適用を並列に行う(lazinessはそのままに)ので、
  ;; 処理に時間かかる関数をmapしたいときはpmapの方が良いかもしれない。
  ;; map-indexedは、要素のインデックスと要素に関数を適用する。
  ;; mapcatは、mapしてconcatする。mapした結果はシーケンスのシーケンスであること。
  ;; map-indexed以外は、引数に複数のシーケンスを指定可能。
  (map inc '(1 2 3)) => '(2 3 4)
  (map + '(1 2 3) '(4 5 6)) => '(5 7 9)
  (map max '(1 2 3) '(4 5 6) '(3 6 2)) => '(4 6 6)
  (map second (sorted-map :a 1 :b 2 :c 3)) => '(1 2 3)
  (pmap inc '(1 2 3)) => '(2 3 4)
  (map-indexed #(vector %1 %2) [:a :b :c]) => '([0 :a] [1 :b] [2 :c])
  (mapcat identity [[1 :a] [2 :b] [3 :c]]) => '(1 :a 2 :b 3 :c)
  (mapcat #(list %1 %2) '(1 2) [:a :b]) => '(1 :a 2 :b)

  ;; keepは、mapと似ているが、関数の結果がnilなら捨てる。
  ;; falseは捨てない。
  ;; keep-indexedは、map-indexedのkeep版。
  (keep next ['(1 2 3) '(1 2) '(1) '() nil]) => '((2 3) (2))
  (keep even? '(1 2 3)) => '(false true false)
  (keep-indexed #(%2 %1) [{0 :a} [0 1 2] #{3 4}]) => '(:a 1)

  ;; file-seqは、あるディレクトリの下のファイル一覧をシーケンス化する。
  ;; 引数はjava.io.Fileオブジェクト。
  (count (file-seq (java.io.File. "C:\\config.sys"))) => 1
  (some #(= (.. % getName toLowerCase) "system32")
    (file-seq (java.io.File. "C:\\windows"))) => true

  ;; line-seqは、テキストファイルの各行をシーケンス化する。
  ;; 引数はjava.io.BufferedReaderオブジェクト。
  (line-seq (java.io.BufferedReader. (java.io.StringReader. "1\n2\n3"))) => '("1" "2" "3")

  ;; re-seqは、正規表現の各マッチ部分をシーケンス化する。
  ;; 自動的にre-groupも適用する。
  (re-seq #"[a-z]+" "Hello, world!") => '("ello" "world")
  (re-seq #"[a-z]+[, ]+([a-z]+)" "Hello, world!") => '(["ello, world" "world"])

  ;; tree-seqは、木を深さ優先で走査したときの各ノード(枝か葉)をシーケンス化する。
  ;; (tree-seq p f t)のようなフォームで、pは枝かどうかを返す述語、
  ;; fは子を取り出す関数、tは木である。
  (tree-seq seq? identity '((1 2 (3)) (4)))      ; ノードがリスト(枝)か数字(葉)の場合。
    => '(((1 2 (3)) (4)) (1 2 (3)) 1 2 (3) 3 (4) 4)

  ;; iterator-seqは、java.util.Iterator<E>を使って得た各要素をシーケンス化する。
  ;; enumeration-seqは、java.util.Enumeration<E>を使って得た各要素をシーケンス化する。
  ;; Iteratorの方がEnumerationより新しいインターフェイスで、反復中に要素の削除が可能。
  (iterator-seq (.iterator (.keySet (java.lang.System/getProperties))))
    => (has some #"java\.vm\.[a-z]+")
  (enumeration-seq (java.util.StringTokenizer. "a b c")) => '("a" "b" "c")

  ;; flattenは、ネストしたシーケンスを平坦にならす。
  (flatten '(1 2 [3 (4 5)])) = '(1 2 3 4 5)

  ;; group-byは、関数の結果によって分類する。
  (group-by odd? '(1 2 3 4 5)) => {false [2 4], true [1 3 5]}
  (group-by :type [{:type :string :data "hello"}
                   {:type :num :data 1}
                   {:type :string :data "world"}])
    => {:num [{:data 1, :type :num}],
        :string [{:data "hello", :type :string} {:data "world", :type :string}]}

  ;; partitionは、シーケンスからn個ずつ取り出したシーケンスを作る。
  ;; (partition n)は、n個ずつに分ける。余ったら捨てる。
  ;; (partition n s p)は、まず先頭からn個取り、次はs番目からn個取る。余ったら捨てる。
  ;; (partition n s p)は、余ったら、pの要素でパディングする。
  ;; partition-allは、partitionの、余っても捨てない版。
  (partition 3 (range 10)) => '((0 1 2) (3 4 5) (6 7 8))
  (partition 2 1 (range 5)) => '((0 1) (1 2) (2 3) (3 4))
  (partition 3 2 [:a :b] (range 5)) => '((0 1 2) (2 3 4) (4 :a :b))
  (partition 3 2 nil (range 5)) => '((0 1 2) (2 3 4) (4))
  (partition-all 3 (range 10)) => '((0 1 2) (3 4 5) (6 7 8) (9))
  (partition-all 3 2 (range 5)) => '((0 1 2) (2 3 4) (4))

  ;; partition-byは、関数の結果によって分割する。
  (partition-by odd? [1 3 5 4 8 7 9 2 6]) => '((1 3 5) (4 8) (7 9) (2 6))

  ;; sequeは、キュー化された遅延シーケンスを作る。
  ;; キュー化されると、遅延シーケンスの最初の何個かの要素が、
  ;; あらかじめ評価される。
  ;; キューのサイズを指定可能。☆デフォルトは不明。
  ;; あるいは、キューとして使うjava.util.concurrent.BlockingQueueオブジェクトを
  ;; 指定しても良い。
  (seque (take 10 (range))) => '(0 1 2 3 4 5 6 7 8 9)
  (seque 3 (take 10 (range))) => '(0 1 2 3 4 5 6 7 8 9)

  ;; reductionsは、reduceの経過をシーケンス化する。
  (reductions + [1 2 3]) => '(1 3 6)
  (reductions #(+ %1 (second %2)) 0 (sorted-map :a 1 :b 2 :c 3)) => '(0 1 3 6)

  ;; to-arrayは、シーケンスをJava配列化する。Object型。
  ;; to-array-2dは、2次元配列にする。
  ;; into-arrayでは、配列要素の型を指定可能。未指定ならシーケンスの第1要素から推測。
  (aget (to-array ["hello" "foo"]) 1) => "foo"
  (aget (to-array-2d [["hello" "foo"] ["buz"]]) 0 1) => "foo"
  (aget (to-array-2d [["hello" "foo"] ["buz"]]) 1 0) => "buz"
  (class (first (into-array ["hello" "foo"]))) => java.lang.String
  (map (memfn toString) (into-array Object [1 true "hello"])) => '("1" "true" "hello")

  ;; dorunは、遅延シーケンスの要素を評価する。
  ;; 評価結果は捨ててnilを返す。つまり、副作用を発動することが目的。
  ;; doallは、評価結果も返す。
  ;; どちらも、どの要素まで評価するか、インデックスで指定可能。
  (with-out-str
    (dorun (take 3 (repeatedly #(print "1"))))) => "111"
  (with-out-str
    (dorun 3 (repeatedly #(print "1")))) => "1111"         ; インデックス3まで評価。
  (with-out-str
    (fact
      (doall (take 3 (repeatedly (fn [] (print "1") true)))) => '(true true true)
    )) => "111"
)

シーケンスの述語

(fact "シーケンスの述語"
  ;; empty?とnot-emptyは、シーケンスにも使える。
  (empty? (rest [1])) => true
  (not-empty (rest [1])) => nil
  (not-empty (rest [1 2])) => '(2)

  ;; realized?は、遅延シーケンスの要素が1つでも評価済みかどうかを返す。
  ;; また、後述のfutureが実施済みかどうか、promiseがdeliver済みかどうか、
  ;; 及びdelayがforce済みかどうかを調べるのにも使う。
  (def lseq (take 3 [1 2 3]))
  (realized? lseq) => false
  (first lseq)
  (realized? lseq) => true
)

検索、正規表現

(fact "検索、正規表現"
  ;; マッチ箇所を返す。
  ;; 捕捉した場合はベクタで返す。
  ;; re-matchesは、要完全一致。
  (re-find #"ab[cde]" "ABC") => nil
  (re-find #"ab[cde]" "abababedc") => "abe"
  (re-find #"ab([cde])" "abababedc") => ["abe" "e"]
  (re-matches #"ab.+f" "abcdefghijk") => nil
  (re-matches #"ab.+k" "abcdefghijk") => "abcdefghijk"
  (re-matches #"ab(.+)f(.+)k" "abcdefghijk") => ["abcdefghijk" "cde" "ghij"]

  ;; 文字列を正規表現へ。
  (re-pattern "ab[cde]") => #"ab[cde]"

  ;; 複雑な検索。
  ;; re-groupsは、直前のre-findの結果を返す。
  (let [matcher (re-matcher #"ab[cde]" "abcabdabe")]
    (re-find matcher) => "abc"
    (re-find matcher) => "abd"
    (re-groups matcher) => "abd"
    (re-find matcher) => "abe"
    )
  (let [matcher (re-matcher #"ab([cde])" "abcabdabe")]
    (re-find matcher) => ["abc" "c"]
    (re-find matcher) => ["abd" "d"]
    (re-groups matcher) => ["abd" "d"]
    (re-find matcher) => ["abe" "e"]
    )
  )

名前空間と外部Libの制御

;; 名前空間を定義し、その中で使いたい外部ライブラリを準備。
(ns my.lib.view
  (:refer-clojure :only (map reduce))
  (:require
    [hoge.lib.core :as hoge]                    ; hoge/hoge-fn1
    [huga.lib.core :as huga :refer (huga-fn1)]  ; huga-fn1
    [piyo.lib core egg leg])                    ; piyo.lib.core/core-fn1
  (:use
    my.lib.util
    [bar.lib.core :only (bar-fn1 bar-fn2 bar-fn3)]
    [buz.lib core view model util])
  (:import
    java.io.File                         ; File/aStaticMethod
    [java.util Date Timer Locale])       ; Date/aStaticMethod
  (:load "view/label" "view/button")     ; viewの下のlabel.cljをインクルード。
  (:gen-class))                          ; 当該名前空間のクラスファイルを生成。

(create-ns 'my.lib.view)
(in-ns 'my.lib.view)
(in-ns 'my.lib.other)
(remove-ns 'my.lib.view)
  • requireは、外部ライブラリを使えるようにする(ただしシンボルの名前空間修飾が必要)
  • referは、外部ライブラリのシンボル名を当該名前空間へ取り込む(名前空間修飾が不要になる)
  • useは、requireしてreferする
  • refer-clojureは、clojure.coreのシンボルに対するrefer
  • importは、Javaのクラス名を当該名前空間へ取り込む(パッケージ修飾が不要になる)
  • gen-classは、Javaクラスを定義する(コンパイル時にクラスファイルを生成する)
(require 'hoge.lib.core)
(refer 'hoge.lib.core)
(use 'hoge.lib.core)
(fact "名前空間"
  ;; 名前空間の実体はclojure.lang.Namespaceオブジェクト。
  (let [clojure-core-ns (the-ns 'clojure.core)]
    (class clojure-core-ns) => clojure.lang.Namespace
    (class *ns*) => clojure.lang.Namespace
    (all-ns) => (contains [clojure-core-ns])
    )

  ;; 文字列化。
  (namespace 'clojure.core/map) => "clojure.core"
  (namespace-munge 'foo-bar.core) => "foo_bar.core"

  ;; 名前空間内の各種マッピング。
  (alias 'zip 'clojure.zip)    ; zip/zipper
  (contains? (ns-aliases *ns*) 'zip) => true
  (ns-unalias *ns* 'zip)
  (contains? (ns-aliases *ns*) 'zip) => false
  ('String (ns-imports *ns*)) => (.getClass "")
  (def hoge nil)
  ('hoge (ns-interns *ns*)) => #'hoge
  ('hoge (ns-map *ns*)) => #'hoge
  (ns-unmap *ns* 'hoge)
  ('hoge (ns-interns *ns*)) => nil
  ('hoge (ns-map *ns*)) => nil
  (ns-name 'clojure.core) => 'clojure.core
  (ns-publics *ns*)  ; the map of all public symbols to vars
  (ns-refers *ns*)   ; the map of all refered symbols to vars

  ;; 名前解決。
  (ns-resolve 'clojure.core 'map) => #'clojure.core/map
  (in-ns 'clojure.core)
  (resolve 'map) => #'clojure.core/map
  )
(fact "外部Libの制御"
  ;; 別ソースのインクルード
  (load "view/label")                             ; クラスパスから
  (load-file "src/view/label.clj")                ; 任意の場所から
  (load-reader (java.io.FileReader. "label.clj")) ; Readerから
  (load-string (slurp "label.clj"))               ; 文字列から
  (some #{'midje.sweet} (loaded-libs)) => truthy
  )

標準入出力とファイルI/O

(future-fact "標準入出力とファイルI/O"
  flush
  printf
  println
  prn
  pr

  println-str
  print-str
  prn-str
  pr-str
  with-out-str

  read
  read-line
  read-string
  with-in-str

  slurp
  spit
  with-open
  )

Struct/Proxy/Protocol

(future-fact "Struct/Proxy/Protocol"
  defstruct
  accessor
  create-struct
  construct-proxy
  defprotocol
  defrecord
  deftype
  extend
  extenders
  extend-protocol
  extends?
  extend-type
  get-proxy-class
  init-proxy
  proxy
  proxy-mappings
  proxy-super
  satisfies?
  struct
  struct-map
  update-proxy
  )

Java Interop

(future-fact "Java Interop"
  import
  .
  new
  set!
  doto
  aclone
  alength
  amap
  areduce
  aset
  aset-boolean
  aset-byte
  aset-char
  aset-double
  aset-float
  aset-int
  aset-long
  aset-short
  bases
  bean
  boolean-array
  booleans
  bytes
  chars
  doubles
  floats
  ints
  longs
  shorts
  byte-array
  char-array
  double-array
  float-array
  int-array
  long-array
  make-array
  object-array
  short-array
  cast
  class?
  comparator
  definterface
  reify

  gen-class
  gen-interface

  ancestors
  derive
  descendants
  isa?
  parents
  make-hierarchy
  supers
  underive
  )

References(mutableなデータ)

(future-fact "References(mutableなデータ)"
  add-watch
  deref
  get-validator
  remove-watch
  set-validator!
  )

Var

(future-fact "Var"
  def
  defonce
  alter-var-root

  ;; 前方宣言。
  (declare some-fn)
  (defn some-caller [] (some-fn))
  (def some-fn (fn [] nil))

  binding
  bound?
  bound-fn
  bound-fn*
  find-var
  get-thread-bindings
  intern
  pop-thread-bindings
  push-thread-bindings
  set!
  test
  thread-bound?
  var
  var?
  var-get
  var-set
  with-bindings
  with-bindings*
  with-local-vars
  with-redefs
  with-redefs-fn
  )

Ref

(future-fact "Ref"
  ref
  alter
  commute
  dosync
  ensure
  io!
  ref-history-count
  ref-max-history
  ref-min-history
  ref-set
  sync
  )

Atom

(future-fact "Atom"
  compare-and-set!
  reset!
  )

Agent

(future-fact "Agent"
  agent
  agent-error
  *agent*
  await
  await1
  await-for
  error-handler
  error-mode
  set-error-handler!
  set-error-mode!
  release-pending-sends
  restart-agent
  send
  send-off
  shutdown-agents
  )

Macro

(future-fact "Macro"
  defmacro
  macroexpand
  macroexpand-1
  )

Multimethod

(future-fact "Multimethod"
  defmethod
  defmulti
  get-method
  methods
  prefer-method
  prefers
  remove-all-methods
  remove-method
  )

並列、並行、非同期処理

(future-fact "並列、並行、非同期処理"
  delay
  delay?
  force
  promise
  deliver
  future
  future?
  future-call
  future-cancel
  future-cancelled?
  future-done?
  locking
  monitor-enter
  monitor-exit
  pcalls
  pvalues
  )

エラー/例外処理

(future-fact "エラー/例外処理"
  *e
  assert
  catch
  throw
  try
  finally
  )

Transientなコレクション

(future-fact "Transientなコレクション"
  transient
  persistent!
  assoc!
  conj!
  disj!
  dissoc!
  pop!
  )

グローバル変数

(future-fact "グローバル変数"
  *clojure-version*

  *ns*
  *command-line-args*

  *compile-files*
  *compile-path*
  *compiler-options*
  *file*
  *unchecked-math*
  *warn-on-reflection*

  *data-readers*
  *default-data-reader-fn*
  *read-eval*

  *in*
  *out*
  *err*
  *flush-on-newline*
  *print-dup*
  *print-length*
  *print-level*
  *print-meta*
  *print-readably*
  )

ビット操作

(future-fact "ビット操作"
  bit-and
  bit-and-not
  bit-clear
  bit-flip
  bit-not
  bit-or
  bit-set
  bit-shift-left
  bit-shift-right
  bit-test
  bit-xor
  )
(bit-and 2r1100 2r1010)   ;=> 1000
(bit-or 2r1100 2r1010)    ;=> 1110
(bit-xor 2r1100 2r0101)   ;=> 1001
(bit-set 2r10000000 2)    ;=> 10000100
(bit-clear 2r11111111 2)  ;=> 11111011
(bit-flip 2r10000000 2)  ;=> 10000100
(bit-test 2r10000000 7)  ;=> true
(bit-shift-left 0x0123deadbeaf4567 8)
                              ;=> 23deadbeaf456700
(bit-shift-right 0x0123deadbeaf4567 8)
                             ;=> 000123deadbeaf45

Misc

(future-fact "Misc"
  clojure-version
  comment
  compile
  do
  eval
  hash
  letfn
  loop
  recur
  trampoline
  quote
  resultset-seq
  time
  xml-seq
  identity
  )
Last modified:2015/05/07 22:03:02
Keyword(s):
References:[FP: 関数型プログラミング]
This page is frozen.