7/10/2011

第5回 SLIMEの使い方 開発サイクルについて

前々回の「基礎編」 ではSLIMEのインストール方法とその基本的な使い方について説明しましたが、 具体的にどのように開発したら良いか分からないと思います。今回はその補足 としてSLIMEにおける開発サイクルについて説明します。

SLIMEにおける開発サイクル

SLIMEにおける開発サイクルは基本的には次のようになります。

  1. M-x slimeでSLIMEを起動
  2. lispファイルを編集
  3. REPLバッファで動作確認
  4. 2に戻る

一般的な開発サイクルには「コンパイル」とか「アプリケーションの再起動」 というタスクがありますが、SLIMEの開発サイクルにはそれがありません。その 代わりSLIMEには、アプリケーションへの変更を即座に反映させる仕組みがあり ます。これにより、待ち時間を極力短縮して、またエディタとターミナルを行 き来することなく、効率的に(また気持ち良く)開発することが可能になりま す。この、いわゆるインクリメンタルなアプリケーションの構築は、SLIMEを使 う最大の利点と言っても過言ではありません。

もっとも、(2)の「lispファイルを編集」するための十分な知識を持っていなけ れば、その利点もあまり感じられないでしょう。本連載でもこの点を重点的に 解説していく予定です。本エントリの後半もその解説になります。

Webアプリケーションなどを開発する際は、(3)は「ブラウザで動作確認」にな ります。もちろんユーティリティ関数のテストやモデルデータの確認をREPLで 行うこともあります。要は使い分けです。

ところで、アプリケーションをインクリメンタルに構築していくという性質上、 何かの拍子に変数の値がおかしくなったり、謎のエラー(というわけではない ですが、原因を考えるのが本質的でないエラー)が発生したりします。そうい う時は、M-x slime-restart-inferior-lispとして開発サイクルを一度リセッ トします。この操作は重要なので是非覚えてください。

バッファでの操作

ここでの操作は上記した開発サイクルの(2)に該当します。SLIMEで一番重要な 操作ですので、是非習得してください。

C-c C-c (slime-compile-defun)

このコマンドは現在ポイントしているトップレベル関数(実は関数以外でも有 効)をコンパイルするコマンドです。コンパイルというと少しヘビーなイメー ジを持ちますが、ここでは関数を定義するぐらいのイメージで問題ありません。 実際に例を示します。まず適当なlispファイルに次のコードを書いてくださ い。

(defun fact (n)
  (if (<= n 1)
      0
      (* n (fact (1- n)))))

次にこのフォームをポイントしてC-c C-cします。コンパイルが成功したとい うメッセージがミニバッファに表示されるはずです。これでfact関数が定義 され利用できるようになりました。

C-c C-zあるいはM-x slime-replとしてREPLバッファを表示し、次のコード を実行してみましょう。

CL-USER> (fact 10)
0

結果が0になっていて、どうもfact関数にバグがあるようです。実はnが1以 下のときに1ではなく0を返してしまっていさう。そこで再びlispファイル を開いて次のように修正してください。

(defun fact (n)
  (if (<= n 1)
      1
      (* n (fact (1- n)))))

そして、先と同じようにC-c C-cします。関数が定義できたらC-c C-zある いはM-x slime-replでREPLバッファを表示し、先と同じコードを実行してみ ましょう。

CL-USER> (fact 10)
3628800

正しい結果が返ってきていますね。この例でも分かるように、バッファでの操 作で一番重要なコマンドはC-c C-cです。関数定義(もっと言えばトップレベ ルフォーム)を編集した際は、C-c C-cする癖を付けるのが良いでしょう。

また、C-c C-cすることで、コンパイラがおかしなコードを拒否したり警告し てくれます。これはバグの早期発見に有効です。試しに次のようなコードを書 いてC-c C-cしてみてください。

(defun f (n)
  (let ((m (* n 2)))
    (1+ n)))

するとlet部分に下線が引かれると思います。この下線は警告を表わしており、 C-x \``あるいはM-x next-error`で警告の内容を詳細表示できます。

cd /home/tomo/tmp/
1 compiler notes:

/home/tomo/tmp/a.lisp:2:3:
  style-warning: Unused lexical variable M

このようにmという変数が未使用であるのが分かりますね。

余力がある方は、明らかにエラーとなるコードを書いてみて、C-c C-cしてみ ることをおすすめします。

C-c C-k (slime-compile-and-load-file)

C-c C-cほどではありませんが、それでも頻繁に使うのがC-c C-kです。こ のコマンドはファイル全体をコンパイルしてロードします。「コンパイル」と か「ロード」はCommon Lispの用語ですが、ここではあまり気にせず、ファイル を先頭から読み直すコマンド、ぐらいのイメージで捉えてください。

C-c C-kは基本的には、まだロードされていないファイルをロードするときに 使います。このとき、C-c C-c同様、コンパイルできないコードはエラーや警 告として視覚的に表示され、C-x \``あるいはM-x next-error`で順番に確認 することができます。

またC-c C-kは、C-c C-cし忘れていないことを確定するのにも便利です。 その他にも対応していない括弧などを検出するのにも使えます。

一般的に、何かおかしくなったらC-c C-kしてみる、それでも駄目なら上記し たM-x slime-restart-inferior-lispする、という運用になります。

C-c C-z (slime-repl)

すでに登場しましたが、このコマンドはREPLバッファを表示します。REPLバッ ファは頻繁に使うことになるので、是非覚えてください。

M-. (slime-edit-definition)

このコマンドは現在ポイントしているシンボルの定義を探すコマンドです。コー ディング時はもちろんのことながら、コードリーディング時やデバッグ時にも 非常に有用です。次に説明するM-,と対で覚えてください。

M-, (slime-pop-find-definition-stack)

このコマンドはM-.を実行したときの場所に戻るコマンドです。M-.で定義 を確認した後に、M-,で元の場所に戻るというのが一般的な使い方です。

REPLでの操作

「バッファでの操作」で説明した操作はREPLでも有効ですが、いくつか追加機 能が用意されているので、それを紹介します。

M-p, M-n

M-p, M-nはそれぞれ前の履歴、次の履歴を補完します。最低限これだけは 記憶してください。同じ式を何度も入力するのはよしましょう。

M-r

このコマンドは正規表現で履歴を検索して補完します。目的の履歴が最初に補 完されない場合は、M-p, M-nで順番に探します。インクリメンタルな検索 が出来れば便利ですが、現状ではそのような機能はないようです。誰か anythingで実装してくれないかな。

特殊な変数

REPL(Top Level Loop)では*, **, ***という特殊な変数が利用できま す。*には前回評価した式の結果が入り、**には前々回、***には前々々 回に評価した式の結果が入ります。評価結果を再利用するときに便利です。簡 単な例を示しておきます。

CL-USER> (+ 1 2)
3
CL-USER> (* * *)
9
CL-USER> (- * **)
6

二番目の式の最初の*は変数参照じゃなくて関数適用です。誤解しないように。 よく分からない人はCommon Lispの入門ページなどで勉強してください。

まとめ

とりあえず今回はここまでです。次回以降は実際に何か作りながら、より実践的な 内容について解説したいと思います。

3 件のコメント:

  1. はじめまして。
    Common Lispの勉強を始めたところでして、毎回参考にしています。
    記事内のコードですが、

    factを再起呼び出しすることろですが、

    (fact (1 - n))
    ではなく
    (fact (- n 1))
    ではないでしょうか?

    返信削除
  2. はじめまして。
    投稿したm2ymの代わりに…

    上の例は、わかりづらいですが、(1 - n) ではなく (1- n) です。
    スペースが違います。

    なのでこれは「関数1-」の呼び出しであり、(- n 1) と等価です。

    返信削除
  3. ありがとうございます。
    「関数1-」でしたか。
    無知な質問でした。

    返信削除