#:g1: com.informatimago.common-lisp.lisp.stepperの紹介

Posted 2014-11-26 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の331日目です。

com.informatimago.common-lisp.lisp.stepperとはなにか

 com.informatimago.common-lisp.lisp.stepperは、Pascal Bourguignon氏作のポータブルなステップ実行のユーティリティです。

パッケージ情報

パッケージ名com.informatimago.common-lisp.lisp.stepper
Quicklisp

インストール方法

(ql:quickload :com.informatimago.common-lisp.lisp.stepper)

試してみる

Common Lispの標準にもstepというステップ実行の為のユーティリティは存在するのですが、SBCLやCCLのようにコンパイル指向の場合は、実行時にソースがないのでstepを実行しても素気ない感じで終わります。
(defun fib (n)
  (if (< n 2)
      n
      (+ (fib (1- n))
         (fib (- n 2)))))

(step (fib 1))

Evaluating call: (FIB 1) With arguments: 1 [Condition of type SB-EXT:STEP-FORM-CONDITION]

Restarts: 0: [STEP-CONTINUE] Resume normal execution 1: [STEP-OUT] Resume stepping after returning from this function 2: [STEP-NEXT] Step over call 3: [STEP-INTO] Step into call 4: [RETRY] Retry SLIME interactive evaluation request. 5: [*ABORT] Return to SLIME's top level. --more--

;⌨ -> 0 ;=> 1

 com.informatimago.common-lisp.lisp.stepperを使えば、コンパイル指向の処理系でもソースを追い掛けた感じのステップ実行が可能です。
可能ですが、ただし専用のパッケージ内で関数を定義する必要があります。

(defpackage :stepper-demo
  (:use :stepper))

(cl:in-package :stepper-demo)

(defun fib (n) (if (< n 2) n (+ (fib (1- n)) (fib (- n 2)))))

という定義をして、stepを実行

(stepper:step (fib 1))

(Will evaluate (fib 1) Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)? ;⌨ -> RET Will evaluate (fib 1) (Will evaluate 1 Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)? ;⌨ -> RET (--> 1)) (Entering function fib (Bind n to 1) Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)? ;⌨ -> RET (Will evaluate (if (< n 2) n (+ (fib #) (fib #))) Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)? ;⌨ -> RET Will evaluate (if (< n 2) n (+ (fib #) (fib #))) (Will evaluate (< n 2) Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)? ;⌨ -> RET Will evaluate (< n 2) (Will evaluate n Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)? ;⌨ -> RET (n ==> 1)) (Will evaluate 2 Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)? ;⌨ -> RET (--> 2)) Evaluation of (< n 2) returned one result ==> T) (Will evaluate n Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)? ;⌨ -> RET (n ==> 1)) Evaluation of (if (< n 2) n (+ (fib #) (fib #))) returned one result ==> 1) Exiting function fib returned one result ==> 1) Evaluation of (fib 1) returned one result ==> 1)

 ちょっとみづらい気がするので改造してみますが、

(defun stepper::will-step (form &optional (stream *step-trace-output*))
  (with-step-printing
    (let ((pos (sb-kernel:charpos stream)))
      (format stream
              "Will evaluate ~&~VT⎛~&~VT⎜  ~S~&~VT⎝~%"
              pos
              pos
              form
              pos))))
(Will evaluate 
 ⎛
 ⎜  (fib 1)
 ⎝
 Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)?
;⌨ -> RET
Will evaluate 
 ⎛
 ⎜  (fib 1)(Will evaluate 
  ⎛
  ⎜  1
  ⎝
  Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)?
;⌨ -> RET
  (--> 1))
 (Entering function fib
   (Bind n                to 1)
  Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)?
;⌨ -> RET
  (Will evaluate 
   ⎛
   ⎜  (if (< n 2) n (+ (fib #) (fib #)))
   ⎝
   Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)?
;⌨ -> RET
Will evaluate 
 ⎛
 ⎜  (if (< n 2) n (+ (fib #) (fib #)))(Will evaluate 
    ⎛
    ⎜  (< n 2)
    ⎝
    Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)?
;⌨ -> RET
Will evaluate 
 ⎛
 ⎜  (< n 2)(Will evaluate 
     ⎛
     ⎜  n
     ⎝
     Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)?
;⌨ -> RET
     (n ==> 1))
    (Will evaluate 
     ⎛
     ⎜  2
     ⎝
     Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)?
;⌨ -> RET
     (--> 2))
    Evaluation of (< n 2) returned one result ==> T)
   (Will evaluate 
    ⎛
    ⎜  n
    ⎝
    Step Into (s, si, RET), Step over (so), Trace (t), Function (f), Run (r), List (l), Eval (e), Debugger (d), Abort (a, q)?
;⌨ -> RET
    (n ==> 1))
   Evaluation of (if (< n 2) n (+ (fib #) (fib #))) returned one result ==> 1)
  Exiting  function fib returned one result ==> 1)
 Evaluation of (fib 1) returned one result ==> 1)

という風に評価される部分で一歩ずつ止まります。

 専用のパッケージで定義する必要があるということから大体想像が付きますが、com.informatimago.common-lisp.lisp.stepperではソースを保存するために必要となる関数にステップ実行の為の仕掛けを入れます。

(defun fib (n)
  (if (< n 2)
      n
      (+ (fib (1- n))
         (fib (- n 2)))))

;==> (stepper:defun fib (n) (stepper:if (cl:< n 2) n (cl:+ (fib (cl:1- n)) (fib (cl:- n 2)))))

まとめ

 今回は、com.informatimago.common-lisp.lisp.stepperを紹介してみました。ステップ実行が苦手な処理系を使っていると、stepも使わなくなりますが、これならデバッグ時に利用できるかもしれないですね。

comments powered by Disqus