// "if"は値を返さないので文だ:
String s;
if (x > 10) {
s = "greater";
} else {
s = "greater or equal";
}
obj.someMethod(s);
// 三項演算子は式だ。なぜなら値を返すからだ。:
obj.someMethod(x > 10 ? "greater" : "greater or equal");
Javaでは、式は値を返すが、文は値を返さない。
// "if"は値を返さないので文だ:
String s;
if (x > 10) {
s = "greater";
} else {
s = "greater or equal";
}
obj.someMethod(s);
// 三項演算子は式だ。なぜなら値を返すからだ。:
obj.someMethod(x > 10 ? "greater" : "greater or equal");
しかしClojureでは、あらゆるものが式なのだ! あらゆるもの が値を返し、複数の式のブロックは最後の値を返す。副作用のみを実行する式は
nil
を返す。
したがって、フロー制御のオペレータもまた式なのだ!
フロー制御のオペレータは組み合わせ可能なので、どこでも使うことができる。これにより、重複コードが減り、中間変数が少なくなる。
フロー制御のオペレータはマクロによって拡張することもできる。マクロはユーザのコードによってコンパイラを拡張することを可能にする。今日はマクロについて議論しないが、詳しくは マクロ 、 Clojure from the Ground Up 、 Clojure for the Brave and True ほか多くの場所で読むことができる。
if
if
は最も重要な制御式であり、条件と"then"と"else"で構成されている。 if
は条件によって選ばれた分岐だけを評価する。
user=> (str "2 is " (if (even? 2) "even" "odd"))
2 is even
user=> (if (true? false) "impossible!") ;; elseは省くこともできる
nil
Clojureでは、すべての値は論理的に真または偽のどちらかだ。 false
と nil
の値だけが「偽」であり、その他のすべての値は論理的に真だ。
user=> (if true :truthy :falsey)
:truthy
user=> (if (Object.) :truthy :falsey) ; オブジェクトは真
:truthy
user=> (if [] :truthy :falsey) ; 空のコレクションは真
:truthy
user=> (if 0 :truthy :falsey) ; 0は真
:truthy
user=> (if false :truthy :falsey)
:falsey
user=> (if nil :truthy :falsey)
:falsey
if
と do
if
は"then"と"else"に単一の式しかとらない。単一の式よりも大きなブロックを作るには do
を使おう。
このようにする唯一の理由は本体部が副作用を持つからであることに注意しよう! (なぜか分かるだろうか?)
(if (even? 5)
(do (println "even")
true)
(do (println "odd")
false))
when
when
は then
の分岐だけを持つ if
だ。条件をチェックし、本体部として任意個の文を評価する(なので do
は必要ない)。
最後の式の値が返る。条件が偽の場合、 nilが返る。
when
は読み手に"else"の分岐がないことを伝える。
(when (neg? x)
(throw (RuntimeException. (str "x must be positive: " x))))
cond
cond
はテストと式が並んだものだ。個々のテストは順に評価され、最初に真になるテストに対応する式が評価されて返る。
(let [x 5]
(cond
(< x 2) "x is less than 2"
(< x 10) "x is less than 10"))
cond
と else
いずれのテストも満たさない場合、nilが返る。最後のテストに :else
を使うのが一般的なイディオムだ。 :else
のようなキーワードは常に真と評価されるため、これが常にデフォルトとして選ばれる。
(let [x 11]
(cond
(< x 2) "x is less than 2"
(< x 10) "x is less than 10"
:else "x is greater than or equal to 10"))
for
forループ ではなく リスト内包表記
シーケンスを順に取り出すためのジェネレータ関数
束縛は doseq
と同じように振る舞う
user=> (for [letter [:a :b]
number (range 3)] ; 0, 1, 2の要素を持つリスト
[letter number])
([:a 0] [:a 1] [:a 2] [:b 0] [:b 1] [:b 2])
Clojureはrecurとシーケンス抽象を提供している
recur
は「古典的な」再帰だ
利用者がコントロールできず、低レベルな機能と考えられる
シーケンスは繰り返しを値として表現する
利用者が部分的に繰り返すことができる
reducerは繰り返しを関数合成として表現する
Clojure 1.5で追加されたが、ここでは扱わない
Java と同じような try
/catch
/finally
(try
(/ 2 1)
(catch ArithmeticException e
"divide by zero")
(finally
(println "cleanup")))