clojure備忘録[replを使った開発手法 その0(ソースファイルの扱い方)]

これまでのエントリで、clojureを書いていく際に必要な基本的情報は説明したつもりです。
が、実際にどのように開発をしていくのかはまだあまりイメージが沸いていないと思います。


以前のエントリではしばしば小さなコード断片(S式)を
replのプロンプトに直接入力して評価してきましたが、
実際の開発では、毎回いちいちプロンプトから入力なんてやってられません。
当然のことながら、ファイルにコードを記述し、
繰り替えし評価できるようにするのが普通です。


今回は最も初歩的なファイルのロードの仕方を紹介します。

★注意事項★

いきなりですが、一つ注意事項があります!(今後増えるかも知れませんが。)
windowsで試している人は、clojureコードの編集に「メモ帳」を使用しないようにしてください。


理由は、clojureソースコードが処理系に読み込まれると、
読み込まれたそばからコンパイルされます。
(特にclassファイルとして出力はされませんけれども。)


故に、clojureのソースファイルはjavaのソースファイルの書き方に習い、
エンコードUTF-8」、「BOMコードは無し」にします。
ところがメモ帳はUTF-8で保存するとファイルの先頭に勝手にBOMコードが挿入されてしまいます。


当然ながらJVMはこのBOMコードに対応していない為、
コンパイルが通らずまったく評価ができません。
なので、clojureコードを記述する際には、


[必須1]エンコードUTF-8を指定できること。
[必須2]その時、BOMを付与せずに保存できること。
[おまけ]括弧の対比が見やすくなる設定がついていること。

の必須条件2つを満たすテキストエディタを使用するようにしてください。
メモ帳は[必須2]の機能が無いため、失格になります。
windowsで試されている方は、サクラエディタ秀丸あたりなら大丈夫だと思います。
unix系の方は多分こだわりのエディタを使用しているでしょう。

sample.cljを作る

では、これからしばらくの間お付き合いする、
clojureのコードを試す為のソースファイルを作成しましょう。


端末エミュレータDOS窓を開き、CLOMINAL_HOMEディレクトリに移動します。
CLOMINAL_HOME直下に、「sample.clj」という名称でファイルを作成してください。
このようになります。


CLOMINAL_HOME
├ lib/
│ ├clojure-1.4.0.jar
│ └clojure-contrib-1.2.0.jar
├ src/
├ repl.sh(またはrepl.bat)
└ sample.clj


次に、今作成したsample.cljをテキストエディタで開き、
1行、次のように記述して保存してください。

(println "Hello, this is 'sample.clj' file.")

何度か出てきていますが、printlnは引数を全て文字列として標準出力に出力する関数です。(末尾に改行が入ります。)
さ、準備が整いました。


load-file関数

replに対し、今コーディングしたsample.cljをロードします。
そのためには「load-file関数」を使用します。

CLOMINAL_HOME直下にいる状態からreplを起動し、
次のS式を評価させてみてください。

(load-file "sample.clj")

次のように表示されれば、sample.cljがロードされ、
評価結果が出力された事になります。

user=> (load-file "sample.clj")
Hello, this is 'sample.clj' file.
nil
user=> 

意図した通り文字列が出力されました。
文字列が出力された後に表示された「nil」は、
println関数の返却値ですので混同しないように注意してください。


では、今度はsample.cljの内容を次のように修正し、保存してください。

(def hello (fn []
             (println "Hello, this is 'sample.clj' file.")))

もう一度replに戻り、先ほどのload-fileのS式を再度評価させてみてください。

user=> (load-file "sample.clj")
#'user/hello
user=> 

今度は文字列が出力されません。


今、sample.cljに書いたコードは、


修正前と同じ文字列を出力する「関数オブジェクト」を、「hello」という変数に束縛する。

というコードです。
評価内容はあくまでも「関数オブジェクトを変数に束縛した」だけなので、
文字列が出力されないのは当然です。
今、sample.cljを評価したことにより、
replのプロセスそのものにhelloが生成されたことになっています。
故に、helloを関数のように呼び出して評価させることができる状態になっています。

replのプロンプトに次のS式を入力して評価させてみてください。

(hello)


変数helloに束縛した関数オブジェクトが評価され、
修正前と同じ文字列が出力されます。

user=> (hello)
Hello, this is 'sample.clj' file.
nil
user=> 


更にいじってみましょう。
今度は呼び出し時に引数として名前を指定すると、
その名前を組み込んだ文字列を出力する関数オブジェクトに修正してみます。
sample.cljを次のように修正し、保存してください。

(def hello (fn [name]
             (println "Hello" name ", this is 'sample.clj' file.")))

例によってload-fileでsample.cljをリロードさせます。

user=> (load-file "sample.clj")
#'user/hello
user=> 

これで以前の引数なしの関数オブジェクトの束縛は、
今新しく定義した引数ありの関数オブジェクトの束縛で上書きされました。
変数helloに新しく束縛された関数オブジェクトを、
引数を指定して評価してみましょう。

user=> (hello "AWACIO")
Hello AWACIO , this is 'sample.clj' file.
nil
user=> 

"Hello" の後ろに指定した名前が挿入されて出力されました。
意図した通りに動作しています。


このように、ソースファイルを使用して開発を行う場合(まー普通いつもこうなると思いますけども)、


「コーディング」
→「ファイルのリロード」
→「動作確認(デバッグ)」
→「コーディング」
→・・・

というサイクルをreplを使いつつ繰り返すことになります。
最近のスクリプト言語perlpythonrubyあたり?)では、
このスタイルでの開発が一般的なんじゃないかなと勝手に思っていますが、
どうなんでしょうかね。
他方で、C、C++C#java当たりの、コンパイルが必要な言語に親しい方にとっては、
結構面食らうスタイルなんじゃないかなと思います。(自分がそうだったので。)



さ、この開発スタイルに慣れる為にも、
また、Lisp的な(あるいは関数型言語的な)考え方に慣れる為にも、
いくつかの非っ常〜によく使用する関数を題材にし、
自前で実装してみましょう。


思いっきり車輪の再発明ですが、
車輪の再発明は「勉強」として効果があることも事実ですので、
どうかお付き合いくださいませ。