CL入門 No.5 - Debugger, Inspector
Debuggerとは?
Common LispではANSI Common Lispの仕様としてデバッガ機能*1の存在があります。
具体的な操作方法や機能に関しては処理系依存となりますが、
一般的によく利用されるデバッガ機能は標準規格化されています。
デバッガへの突入
プログラム中でコンディション*2が発生すると、
通常はハンドラ*3が捕捉して解決しますが、ハンドラが捕捉しなかった場合、
デバッガが受け取り対話モードが開始されます。
下記のコードをREPL上で試してみましょう。
(error "welcome to underground...")
error関数は、エラーコンディションを発生させる関数です。
コンディションハンドラも設定されていないので、デバッガがコンディションを受け取ります。
SBCLのデバッガ
Common Lispの処理系にはデバッガ必ず付属しています。
SBCLのデバッガを上のコードで立ち上げると、次のような表示がされるでしょう。
debugger invoked on a SIMPLE-ERROR in thread #<THREAD "main thread" RUNNING {1002A712E3}>: welcome to underground... Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL. restarts (invokable by number or by possibly-abbreviated name): 0: [ABORT] Exit debugger, returning to top level. (SB-INT:SIMPLE-EVAL-IN-LEXENV (ERROR "welcome to underground...") #<NULL-LEXENV>) 0]
主要な操作は次のようなものです。
- ? - 操作方法を表示
- 数字 - 対応するリスタート*4
- 式 - (REPL特有の機能などは制限されるものの)ほぼ任意を実行できる
実際のデバッグではステップ実行やバックトレースが役に立ちますが、
デバッグ機能の詳しい説明は後々の記事にまとめます。
今回はデバッガに入るとリスタートできるということだけ覚えてみましょう。
REPLを立ち上げて、おもむろに下のコードを打ってみましょう。
(defun hoge () (+ 10 *hige*))
コンパイルは通りますが*hige*は未定義の変数なので警告*5が出ます。
続けて定義したhoge関数を実行してみましょう。
(hoge)
すると次のようなに出力されデバッガが立ち上がります。
debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD "main thread" RUNNING {1002A712E3}>: The variable *HIGE* is unbound. Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL. restarts (invokable by number or by possibly-abbreviated name): 0: [ABORT] Exit debugger, returning to top level. (HOGE) 0]
ここで「0」と入力すると番号0のリスタートが実行されます。
するとtop-level*6に戻るので、REPLの入力から再スタートすることができます。
Slimeのデバッガ(SLDB)
Slime環境で開発する場合、
SLDBという強力なデバッガを使用することができます。
先ほどと同様にREPLを立ち上げたら、次のように打ってみましょう。
(defun hoge () (+ 10 *hige*)) (hoge)
同様にやはり*hige*が未束縛なので、
コンディションが発生し、デバッガが立ち上がります。
The variable *HIGE* is unbound. [Condition of type UNBOUND-VARIABLE] Restarts: 0: [RETRY] Retry SLIME REPL evaluation request. 1: [*ABORT] Return to SLIME's top level. 2: [ABORT] Abort thread (#<THREAD "repl-thread" RUNNING {1003F38063}>) Backtrace: 0: (HOGE) 1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (HOGE) #<NULL-LEXENV>) 2: (EVAL (HOGE)) --more--
SBCLに比べてリスタートの種類が増えています。
Slime環境では、サーバクライアントモデルで応対するので、
REPLのコマンドはSLIME REPLからのプログラムとして処理されます。
よってREPLのリクエストからリスタートできるように作られています。
- 0番リスタート - REPLの式を再実行する
- 1番リスタート - SlimeのREPLに戻る
デバッガが立ち上がったら「:」を入力してみましょう。
するとSlime Evalが立ち上がり任意の式を実行できます。
(defvar *hige* 20)
上の式を実行してから0番のリスタートから再開しましょう。
今度は*hige*が定義されているので、関数が正しく評価されます。
Inspectとは?
Inspectはオブジェクトを閲覧しながら編集することができる機能です。
Inspectも操作方法や機能に関しては処理系依存となります。
言語処理系のInspectorを起動したい場合は、inspect関数を呼び出します。
SBCLのInspect
SBCLのREPLを立ち上げたら、次のように入力してみましょう。
(defvar *hoge* '(10 20 30)) (inspect *hoge*)
Inspectが立ち上がり、次のように表示されます。
The object is a proper list of length 3. 0. 0: 10 1. 1: 20 2. 2: 30 >
Inspectの操作方法は標準Common Lispでは規定はありませんが、
おおよその処理系でhelpまたは?と入力すると、
操作説明が出力されるように作られています。*7
SBCLの場合、主に使うInspectコマンドは下記のものです。
- ? - 操作ヘルプの表示
- q, e - Inspectの終了
- 数字 - 番号のオブジェクトに進入
- u - 一つ前のオブジェクトに戻る
- 式 - 任意の式を評価する
Debugger同様、Inspect中でも任意の式を評価できます。
SBCLの場合、
SB-EXT:*INSPECTED*にInspectされているオブジェクトが束縛されるので、
setfなどを利用してオブジェクトを編集することができます。
例えば上記のInspect中に、
(setf (elt *inspected* 1) 100)
を評価すると、リストの2番目のオブジェクトが100に変更されます。
rを入力してInspectの再表示をして確認してみましょう。
その後qでInspectから抜けた後に、
*hoge*
を評価して、Inspectを終了しても
オブジェクトの変更が反映されていることを確認できます。
Slime-Inspector
Slimeには特有のInspectorがあります。
Common Lispの処理系に依存しないでInspectを利用したい場合に便利です。
M-x Slime-InspectでInspectorを起動できます。
Slimeの場合、処理系標準のInspectのように、
Inspectされているオブジェクトを直接何らかの変数にバインドしません。
代わりに、Inspectされているオブジェクトを「*」*8に束縛する機能があります。
Slime-Inspectorの主な操作は下記のようになります。
- q - Inspectorの終了
- l - 一つ前のオブジェクトに戻る
- M-RET - *にInspectしているオブジェクト束縛する
おわりに
ここまでで、代表的なCommon Lispの開発環境の説明は終了です。
あくまでツールの基本的な使い方のみ解説しましたので、
応用編は随時記事にしていく予定です。
次の記事からはCommon Lispでのプログラミングを始めます。