最近 Clojure を完全にマスターした方が増えてきた。
http://d.hatena.ne.jp/kamekoopa/20150910/1441860552
http://teafortw0.hatenablog.com/entry/2015/09/13/224950
恥ずかしながら Clojure を業務で半年も書いているというのに、僕は Clojure を完全にマスターしたことがありませんでした。
なので、今回 Clojure を完全にマスターしようと思います。完全マスターマスターの kamekoopa 氏によると完全マスターとは「 HelloWorld やったり普通の FizzBuzz やったり出来た、を指す隠語」とのことなので、今回はこの定義に則って Clojure を完全にマスターします。
環境
今回使う環境は以下の通り。
- XUbuntu 14.04 LTS
- Java 1.8.0_60
- Clojure 1.7
- Leiningen 2.5.2
- Emacs 24.5.1
Emacs の準備
Clojure と言えば Emacs と言われますが個人的に Emacs 使えない人には IntelliJ IDEA と Cursive を推します。僕は Emacs を使います。
ということで Emacs のインストールからはじめましょう。
$ sudo apt-get install emacs
これで Emacs をインストールすることが出来ました。簡単ですね。
次に Emacs で Clojure を書くための設定をしないといけないのですが、 0 から設定するとめんどくさいので既存のものを拝借しましょう。
上のリポジトリを次のように clone してきます。
$ cd ~/
$ git clone https://github.com/flyingmachine/emacs-for-clojure .emacs.d
もし、既に Emacs を使っている場合、一度自分の設定を退避させるなどしておくといいでしょう。
ここまで出来たら、 Emacs を起動させましょう。
$ emacs
初回は色々とダウンロードされてコンパイルされたりしますが、落ち着いて眺めておくと終わると思います。
いい感じの黒い画面が出たら完了です。簡単ですね。
ここまで出来たら一旦 Emacs を消しておきましょう。 C-x C-c
で終了できます。
Leiningen の準備
次に Clojure と言えば Leiningen ですね。説明は他に譲りますが、簡単に言えば Clojure 向けのビルドツール兼パッケージツールです。
~/bin
にパスが通っているという前提になりますが以下のようなコマンドで簡単にインストールできます。
$ wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein
$ mv lein ~/bin
$ chmod u+x ~/bin/lein
$ lein
ref: http://clojournal.com/entry/54e42e79e4b07686596991c1
最後に次のコマンドでちゃんとインストール出来ていることを確認しましょう。
$ lein -v
この記事を書いている時点での最新は 2.5.2 です。
更に ~/.lein/profiles.clj
を開き、次のコードをペーストします。
{:user {:plugins [[cider/cider-nrepl "0.8.1"]]}}
今回ちょっと Emacs の設定をサボった都合上、 Cider のバージョンを下げないとワーニングが出るのでこのような設定になりますが、ひとまずはいいでしょう。
Hello, world する
まずは簡単にプロジェクトを作成しましょう。
$ lein new perfect-master
$ cd perfect-master
$ emacs
という具合で perfect-master というプロジェクトを作成します。その後、プロジェクトルートへ移動し Emacs を起動しています。
C-x C-f
としてミニバッファ(画面下の枠)にパスが出てくるので any-dir/perfect_master/src/perfect_master/core.clj
と入力していきます。 core.clj
を開いたら C-c M-j
と入力して cider-jack-in します。
しばらく待つと画面が分割されて、カーソルが REPL 上にあると思うので次のように入力します。
(println "Hello, world")
閉じ括弧の後ろで C-m
or Enter
と入力すると、 Hello, world と出力されるのが確認出来たと思います。
完全マスターにこれで一歩近づきました。簡単ですね。
FizzBuzz する
REPL にカーソルが入ってると思うので C-x o
で core.clj
へと移ります。
foo
関数が定義されていると思いますが、これは邪魔なので削除します。
(defn foo
"I don't do a whole lot."
[x]
(println x "Hello, World!"))
この関数の開き括弧のところで C-k
などと押すとさくっと全て消えてくれます。簡単ですね。
これは paredit の機能でこのような動きをしますが、この機能によって括弧の対応をあまり気にする必要がなくなるので Clojure を書くなら必須だと思います。
まず最初に次のようなコードを書いてみます。
(defn fizzbuzz [n])
次に閉じ括弧の後ろで C-x C-e
と入力します。これで REPL 上の perfect-master.core
ネームスペースに fizzbuzz
関数が定義されました。
REPL を見ると user>
と出ているので現在のネームスペースが user
であることが分かります。これを perfect-master.core
ネームスペースに変えましょう。 core.clj
にカーソルがある状態で C-c M-n
と押すと、 REPL 上のネームスペースが perfect-master.core
へと変わりました。次に REPL へと移動して (C-x o
) 次のコードを評価します。
(fizzbuzz 10)
すると nil
が返されます。これは関数の定義が存在するけど、関数の中身が定義されていないのでこのようになります。ともあれ、これで fizzbuzz
関数が定義されたのは確認出来ました。
次に core.clj
へと戻りファイルに直接 (fizzbuzz 10)
と書いてやはり閉じ括弧の後ろで C-x C-e
としてみましょう。
するとミニバッファへ nil
と出てきます。わざわざ REPL に移動しなくてもこのようにミニバッファで評価した式の結果を簡単に見ることもできます。簡単ですね。
さて、書いたコードの評価の仕方を覚えたところで少しずつコードを書いていきましょう。まず仕様ですが、次のような結果になるような関数を作りたいと思います。
(fizzbuzz 20) ;=> (1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz)
小さく作っていきたいと思うので、まずは 1 から n までの整数列を返すようにしてみましょう。これは range
関数を使えば良さそうです。すると次のようになります。
(defn fizzbuzz [n]
(range 1 (inc n)))
これを評価して (fizzbuzz 20)
を評価すると (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
とミニバッファに(あるいは REPL で評価したなら REPL に)と出力されたと思います。
次に fizzbuzz
のコアとなる数字を割り切れたら文字列に変換する部分を書きます。これは単機能として独立させることが出来るので別関数 fizzbuzz*
を次のように定義し評価します。
(defn fizzbuzz* [n]
(if (zero? (rem n 15))
"FizzBuzz"
(if (zero? (rem n 3))
"Fizz"
(if (zero? (rem n 5))
"Buzz"
n))))
基本的な if
とちょっとした関数を使っただけのバージョンです。ちょっと野暮ったいですがまぁいいでしょう。これはひとつの数字に対して機能するものなので、試しに (fizzbuzz* 15)
などと評価すると FizzBuzz
と返ってきたと思います。
こうすると整数列を作るのと文字列へと変換する関数が出来たことになります。となると、これらの機能を組み合わせるといい感じに出来そうです。
これには map
関数を使いましょう。
(defn fizzbuzz [n]
(map fizzbuzz* (range 1 (inc n))))
このように最初の fizzbuzz
関数を修正し評価したあとに、 (fizzbuzz 20)
を評価すると最初に書いた期待値である (1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz)
がミニバッファに出力されたと思います。
ここまでとても簡単でしたね。
テストを書く
だいたい Emacs で Clojure を書く流れが分かってきたので調子に乗ってテストを書いてみます。
perfect_master/test/perfect_master/core_test.clj
を開きます。 a-test
とあるので消しておきます。
そして次のようなコードを書きます。
(deftest fizzbuzz*-test
(testing "fizzbuzz test"
(is (= (fizzbuzz* 1) 1))
(is (= (fizzbuzz* 3) "Fizz"))
(is (= (fizzbuzz* 5) "Buzz"))
(is (= (fizzbuzz* 15) "FizzBuzz"))))
これも今まで同様に評価します。評価した後、テストの実行は C-c ,
で出来、テストが通ればミニバッファが緑色になります。
コケた場合は次のようなエラーメッセージが表示されます。
Test Summary
perfect-master.core-test
Ran 4 tests, in 1 test functions
1 failures
Results
Fail in fizzbuzz*-test
expected: (= (fizzbuzz* 15) "FizzBuz")
actual: (not (= "FizzBuzz" "FizzBuz"))
REPL が開いてあった窓に出力され、カーソルがエラーメッセージの方にいきますが q
と入力するとこのエラーメッセージを閉じることができます。またコケるとテストコードのコケたテストの部分がハイライトされるので分かりやすいです。
リファクタリング
野暮ったいコードを書いてしまったので綺麗にします。
core.clj
に移動して次のように fizzbuzz*
関数を書き直して C-c ,
とテストを実行して通るのを確認します。
(defn fizzbuzz* [n]
(condp #(zero? (rem %2 %1)) n
15 "FizzBuzz"
3 "Fizz"
5 "Buzz"
n))
このとき少しずつ書いて、評価してテストを実行するというのを繰り返すと Clojure の面白さが分かるかもしれないです。
完全にマスターした
という感じで Clojure 完全にマスターしました。
おまけ
今回全く画像を用意していないので、ちょっと分かりにくいかもしれないけどコード書いて評価したりテストしたりしてる様子はこの辺とか参考になります。
https://asciinema.org/a/26400
https://www.youtube.com/watch?v=iazvlNYjtaM
Emacs の Cider-mode の簡易設定は今度時間があるときに適当に作って公開したいと思います。
また、 Cursive のユーザーガイドを翻訳しています。
(本当は作者の Colin Fleming からはドキュメントのアップデートを沢山しないといけないところがあるから、翻訳するのはその後にして欲しいって言われてるのであまり公にするつもりなかったんですが、すっかり公になってしまってるのでとりあえず当時翻訳してたものをそのまま出しています。今後、大きいドキュメントの更新があるまで僕はこれを修正するつもりはあまりないですが、とりあえず参考にはなるかと思ってこのままにしておきます。ちなみに公式のユーザーガイドはライセンスがまだ明記されていないので、一応勝手翻訳ということになってます( Colin は EPL か MIT とかにしたいと言ってましたが、恐らくドキュメントが綺麗に整備されるときに一緒にそのへんも整備されるはずです))