暇を持て余しているJava廃人はClojureで遊ぼう
準備
- ClojureDocs (API、サンプルコード参照)
- Clojure Japanese Documentation (日本語ドキュメント。詳しく知りたいときに)
- Light Table (エディタ、実行環境)
- Leiningen (Mavenのようなもの。あとでいい)
Light Table 動作確認
- プロキシ環境の場合、環境変数HTTP_PROXYとHTTPS_PROXYを事前に設定
- 新しいファイルを作成
- ctrl-space
- コマンドバーが開く
- syn
- Syntax設定
- clo
- Clojure選択
- エディタで適当にコード入力
- (map - (range 10))
- ctrl-enter (cmd-enter)
- コード実行
- 関数実行を止めたいときは、ctrl-space後、cancelかdisconnectでコマンド探す
基本のデータ型
- 有理数のリテラルがある(2/3など)
- 基数(radix)として2〜36まで指定できる数値リテラルがある(2r101、36rxyZなど)
- 整数はLong、浮動小数点数はDoubleになる
- NをつけるとBigI n t、MをつけるとBigDeci m al
- 項目はスペースで区切る。カンマはスペースと同じ
- 文字列の連結は
strで。\aなどで文字を表す
- 特殊な文字として\return \newline \tab \space \backspace \formfeedがある
- defで変数定義(Clojureは基本的にイミュータブルなので変更されないけど)
- 文字列の一致比較は
=でできる
- 文字列と似て非なるものとしてキーワードがある(Rubyにあるシンボルのようなもの)。
:abcのようにコロンを先頭につける - 文字列と違い、連結や切り出しはできない。また文字列は文字のシーケンスとして扱えるが、キーワードはシーケンスではない
シーケンス、コレクション
- リスト(LinkedListのようなもの)
(1 2 3)
- ベクタ(ArrayListのようなもの)
[2 3 4]
- マップ(HashMapのようなもの。LinkedHashMapではない)
{:a 1 :b 2}
- セット(HashSetのようなもの。LinkedHashSetではない)
#{:a :b :c}
- これらはシーケンスという抽象化した構造として様々な関数で統一的に扱うことができる。
- マップはclojure.lang.MapEntryのシーケンスとなる。MapEntryはclojure.lang.IPersistentVectorであるためベクタでもあり、java.util.Map.Entryでもある
- なお、Iterableもシーケンスとして扱える
シンボルとクオート、評価
- リストは中身が1つ以上のときに評価されると、先頭の要素が関数として扱われるため
(1 2 3)のようなリストを実行(評価)すると例外が発生する
- 例外を発生させないためにはクオートをつけて、評価を1回行わないようにすることができる
-
(str :a :b)のようなコードのstrの部分はシンボル(clojure.lang.Symbol)であり、シンボルが関数として評価されると現在の環境から解決された(特定された)関数が実行される -
クオートをつけると、その部分全体の評価が行われないので
(def v 9) '[1 v]の結果は、[1 9]ではなく[1 v]になるというところに注意
変数と関数の定義
- 関数は
defnで定義できる -
:preと:postで複数個の事前条件と事後条件(%が戻り値を表す)を指定できる
- 関数では複数の引数定義を行える
-
&の後ろは可変長引数を表す
- privateな関数は
defn-で、privateな変数はdef ^:privateで定義できる - 関数内のローカル変数は
letで定義できる
Javaメソッド呼び出し
-
(Math/pow 2 3)のようにjava.langパッケージにある(Clojure 1.6.0ではJava 6に存在する)クラスのstaticメソッドはimportなしに呼び出せる - インスタンスメソッドはメソッド名の前に
.をつけて呼び出せる - importをするとjava.lang以外のパッケージのクラス名を(パッケージ名つけずに)使える
- クラス名の後に
.をつけるか、newを使うことで新しいオブジェクトを構築できる
- インスタンスメソッドは
.を使っても呼び出せる -
..を使うとメソッド呼び出しを連鎖できる -
dotoを使うと同じオブジェクトに対する複数回メソッド呼び出しが簡単にできる
filter, map, reduce, for
-
filterとremoveは逆の動作をする -
fnで無名関数を作れる
-
#(...)の形式でも無名関数を作れる。%または%1が第一引数、%2が第2引数、%&は残りの引数を表す -
keepはmapと似ているが、値がnilとなる結果は含まれない。
-
reduceには初期値を与えることもできる -
reduceを途中で止めたい場合はreducedを使える -
ifやwhenなどの条件で"真"と判定されるのはfalseとnil以外のすべて
- 多重ループ的な処理をしたい場合は、
forが便利 -
:whileは入れる場所によって動作が変わるので注意
- 結果をベクタにする
filterv,mapvもある
スレッディングマクロ
-
->>,->,as->などを用いることで、深いネスト構造の式をフラットに記述することができる
副作用と遅延シーケンス
- 関数本体や
let本体など、複数の式が書ける場所ではprintlnなどの副作用がある式をそのまま挟み込める -
doを使えば、どこにでも副作用がある式を入れられる。(do全体の結果は最後の式の結果となる)
-
printlnによってConsoleには以下のような表示がされる
- Javaのオブジェクトに対する操作をするときもそうだが、Clojureのオブジェクトでも
atom,ref,with-local-varsなどを用いて副作用がある操作をすることがある - Light Tableで式の途中の値が知りたい場合は、Watchの機能が使える
-
mapやfilterなどの関数では結果は遅延シーケンスとして返されるため、スレッドローカルな値を扱う関数と組み合わせると期待した値にならないことがある
- 遅延シーケンスを即時評価するには
doallを使うか、ベクタやマップに変換するなどの手が使える
- 副作用だけが必要な場合は
doallの代わりにdorunが使える - 無限(遅延)シーケンスを評価するときには
takeなどでその一部を取得する
分配束縛
-
letやdefnなど変数に値を束縛するところでは分配束縛ができる -
:asを使うとそのベクタ全体の値を束縛できる。(defn直下のベクタでは:asは使えない)
-
:keysを使うとキーワード引数のような関数呼び出しを扱える。:orでデフォルト値を指定できる
-
:keysの代わりに:strsと:symsで文字列とシンボルも扱えるがあまり使われない(?)
ネームスペース
-
nsでネームスペースを定義できる。:requireでClojureのネームスペースを利用。:importでJavaのパッケージを利用
入出力と正規表現
-
spitとslurpで文字列の書き込み、読み込みができる -
:appendなどのオプションを指定できる。:encodingでエンコーディング指定可能(デフォルトはUTF-8)
- clojure.java.ioの
writerとreaderを使うとBufferedWriter, BufferedReaderでの処理ができる - 確実にcloseするために、
with-openを使用している -
line-seqは遅延シーケンスを返すため、with-openの外まで遅延したまま持って来れないことに注意
-
re-findとre-seqで正規表現マッチができる。 - 大文字小文字の区別を排除するには(java.util.regex.Patternで定義済みの)
(?i)などの埋め込みフラグ表現を使う
マクロ
- 制御構造の操作を伴う処理をしたい場合などにマクロを定義する
- マクロの本体にシンタックスクオート
`をつけ、マクロの引数にアンクオート~をつけるのがまずは簡単な作り方
- マクロの展開結果を知りたいときは、
macroexpand-1などを使う - Light Tableのkeymapに
"ctrl-e" [(:eval.custom "(macroexpand-1 '__SELECTION__)")]などを登録すると選択箇所のマクロ展開に便利
- マクロ本体で変数を使う場合、変数名の後に
#をつけてユニークな名前にする。(gensymされる) - スプライシングアンクオート
~@を使うとリストが展開されてからアンクオートされる
Leiningenの使用
- REPLの起動
lein repl-
(doc map)や(source filter)でドキュメントやソースを表示 - Light Tableでソース見たいときはkeymapに
"alt-s" [(:eval.custom "(with-out-str (clojure.repl/source __SELECTION__))" {:verbatim true})]などを登録すると便利
-
- プロジェクトの作成
lein new app myproject1- lein new
テンプレート名プロジェクト名の形式。テンプレート名は省略するとデフォルトのテンプレート(ライブラリ用?)が使用される。appはアプリケーション用 - 生成されたproject.cljの
:dependenciesで依存するライブラリを記述(Maven, Clojarsリポジトリから自動ダウンロード)
- lein new
- standalone.jarの作成
lein uberjar- (Clojureのjarも含む)すべての依存ライブラリをまとめた単独実行可能なjarファイルを作成する
- ヘルプ
lein help
lein help new
lein help uberjar...
よく使う関数
よく使う関数で説明をあまりしてないものはこんなところでしょうか。
- apply
- cond, if-let, when-let
- some, every?
- first, second, last
- cons, concat, list*
- mapcat, map-indexed, keep-indexed
- repeat, repeatedly, cycle
- interleave, partition
- identity, comp, partial
- take, take-last, drop, drop-last
他にもよく使う関数あるけど、まず取っ掛かりとしてこれだけわかればClojureで簡単なプログラム書けるでしょう〜





































