#:g1: cut-inの紹介

Posted 2014-05-04 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の125日目です。

cut-inとはなにか

 cut-inは、超手前味噌ですが、g000001作のデバッグユーティリティです。

パッケージ情報

パッケージ名cut-in
Quicklisp×
参考サイトg000001/cut-in · GitHub

インストール方法

 GitHubからダウンロードしてきて、Quicklispのlocal-projecsに置いておけば、

(ql:quickload :cut-in)

でロードできると思います。微妙にSBCLにしか対応していません。

試してみる

 思い付きでprintデバッグを支援するユーティリティを作ってみました。cut-inという名前が適切なのかは疑問です。
当初は、Gaucheの#?=を再現するつもりでいましたが、CL:BREAKしたいなと思い始めた辺りから脱線して無駄な機能拡張をしてしまいました。
記法は、#>>>が基本で、#>から>>までの間にオプションが書けます。
リードテーブルはnamed-readtablesで扱えるようにしているのでin-readtableかなにかして使います。

(named-readtables:in-readtable :cut-in)

printデバッグ

 #>>>だけで何もオプションを付けなければ、返り値をプリントします。一応多値にも対応しているので、多値のフォームの間に挟んでも二値目以降が無くなったりはしません。

(defun mahaman (n)
  (list n (* n n) (list #>>>(* n n n))))

(mahaman 8) ;;; file:///foo.lisp:745 ;;; ;;; (* N N N) => 512 ;=> (8 64 (512))

file://... という行がありますが、emacsで該当場所にジャンプしたら便利かなと思ってファイルの場所を表示するようにしてみました。

(defun cut-in-jump-to-cut-in-position (name &optional where)
  (when (string-match "\bfile://\(.*\):\([0-9]+\)" name)
    (select-window
     (get-buffer-window
      (find-buffer-visiting
       (substring name
                  (match-beginning 1)
                  (match-end 1)))))
    (goto-char
     (parse-integer 
      (substring name
                 (match-beginning 2)
                 (match-end 2))))
    (re-search-forward "#>")))

のような適当な関数を定義して、

(add-to-list 'slime-edit-definition-hooks #'cut-in-jump-to-cut-in-position)

とすると、file://...の上でM-.すると、該当場所に微妙に近かったり遠かったりするところに飛びます。

break

 CL:BREAKしてみたらどうかなと思ったのでオプションを付けてみました。

(defun kandi (n)
  (list n (* n n) (list #>break>>(* n n n))))

(kandi 42) ;;; (* N N N) => 74088 ; file:///foo.lisp:888 ; [Condition of type SIMPLE-CONDITION] ; ; Restarts: ; 0: [CONTINUE] Return from BREAK. ; 1: [RETRY] Retry SLIME evaluation request. ; 2: [*ABORT] Return to SLIME's top level. ; 3: [ABORT] Abort thread (#<THREAD "worker" RUNNING {10165561B3}>) ; ; Backtrace: ; 0: (KANDI 42) ; 1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (KANDI 42) #<NULL-LEXENV>) ; 2: (EVAL (KANDI 42)) ; 3: ((LAMBDA NIL :IN SWANK:EVAL-AND-GRAB-OUTPUT)) ;> 0 ;[CONTINUE] ;=> (42 1764 (74088))

time

 多分使わないと思いますが、CL:TIMEしてみたらどうかなと思ったのでオプションを付けてみました。

(defun lorto (n)
  (list n (* n n) (list #>time>>(* n n n))))

(lorto 42) ;;; file:///foo.lisp:1540 ;;; ;;; (* N N N) => 74088 Evaluation took: 0.000 seconds of real time 0.000000 seconds of total run time (0.000000 user, 0.000000 system) 100.00% CPU 1,962 processor cycles 0 bytes consed ;=> (42 1764 (74088))

型宣言

 コードにしっかり書くべきな気がするので、多分使わないと思いますが、型宣言を入れてみたらどうかなと思ったのでオプションを付けてみました。フォームをCL:THEで包むことになります。

(defun madalto (n)
  #> (values t t t float) >>
  (values 1 2 3 n))

(madalto 8) The value 8 is not of type FLOAT. [Condition of type TYPE-ERROR]

Restarts: ...

関数

 fboundp => Tなシンボルの場合、値に関数を適用した結果を表示します。多値にも対応してみましたが、どうなんでしょう。

(defun dalto (n)
  (list n #>describe>>(* n n) (list (* n n n))))

(dalto 42) ;;; file:///foo.lisp:2091 ;;; ;;; (* N N) => 1764 1764 [fixnum] ;=> (42 1764 (74088))

無名関数

 λが書きたくなることがあるかなと思って機能追加してみました。

(defun porfic (n)
  #> (lambda (x) (print (type-of x))) >>
  (values n (* n n) (list (* n n n))))

(porfic 42) ;;; file:///foo.lisp:2322 ;;; ;;; (VALUES N (* N N) (LIST (* N N N))) => 42, 1764, (74088)

(INTEGER 0 4611686018427387903) (INTEGER 0 4611686018427387903) CONS

;=> 42 ; 1764 ; (74088)

nop

 nopを入れると空振りもできます。

(defun porfic (n)
  #> nop (lambda (x) (print (type-of x))) >>
  (values n (* n n) (list (* n n n))))

(porfic 8) ;=> 8 ; 64 ; (512)

まとめ

 今回は、cut-inを紹介してみました。
無名関数が使える位まで拡張したところで、振り出しに戻った気がしたので、とりあえず完成としました。

comments powered by Disqus