Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2010-12-29

*macroexpand-hook*の使い道

| 19:26 | *macroexpand-hook*の使い道 - わだばLisperになる を含むブックマーク はてなブックマーク - *macroexpand-hook*の使い道 - わだばLisperになる

以前から、*macroexpand-hook*ってどんな使い道があるのだろうかと謎に思っていたのですが、Spice Lisp(TOPS-10 CL)のソースを眺めていて、macromemoというファイルを発見し、その中で*macroexpand-hook*が使われているのを見付けました。

マクロ展開関数の引数が2つのようなので、ANSI CL的に3つに揃えてみると、

(defun memoize-macro-call (expander expression env)
  "Replaces the call to a macro in Expression with a call to the expanded form
  with magic stuff wrapped around it."
  (let ((expansion (funcall expander expression env)))
    (if (eq (car expression) '*macroexpansion*) nil  ; "unless" is a macro...
	(displace expression (list '*macroexpansion* expansion
				   (cons (car expression) (cdr expression)))))
    expansion))

(defun displace (x y)
  "Replaces the CAR and CDR of X with the CAR and CDR of Y, returning the
  modified X."
  (rplaca x (car y))
  (rplacd x (cdr y)))

(defmacro *macroexpansion* (expansion original)
  (declare (ignore original))
  expansion)

(setq *macroexpand-hook* 'memoize-macro-call)

こんな感じになります。

動作させてみると、

;; 下準備
(defmacro foo (x)
  `(progn ,x))

(defmacro bar (y)
  `(progn ,y (foo 42)))

;; フックを設定
(setq *macroexpand-hook* 'memoize-macro-call)

(defparameter *expr* (copy-tree '(bar 7)))

(macroexpand *expr*)
;=> (PROGN 7 (*MACROEXPANSION* (PROGN 42) (FOO 42)))
;   T

*expr*
;=> (*MACROEXPANSION* (PROGN 7 (*MACROEXPANSION* (PROGN 42) (FOO 42))) (BAR 7))

;; 通常に戻す
(setq *macroexpand-hook* 'funcall)

(defparameter *expr* (copy-tree '(bar 7)))

;; 破壊的に書き変わっているので再度定義
(defmacro foo (x)
  `(progn ,x))

(defmacro bar (y)
  `(progn ,y (foo 42)))

(macroexpand *expr*)
;=> (PROGN 7 (FOO 42))
;   T

*expr*
;=> (BAR 7)

MacLISP等には、マクロ関係の機能で、displaceというのがあり、都度マクロを展開するのではなく、一度展開したものは、前の結果に置き換え、というものがあったようです。(ちなみに古いコードが残っているMaximaにもあります)

上記のコードは、実際の動作が掴みにくいのですが、一度展開されると同じものが返っているのが分かります。

現在は、コンパイラ指向の処理系が多いので効果は薄そうで、むしろ副作用の方が気になりますが、インタプリタでは有効なテクニックだったりしたのかもしれません。はっきりしたことは分かりませんが…。

マクロのメモ化も面白いと思って良くあるタイプのハッシュテーブルを使ったものに書き換えてみようかなとも思いましたが、引数の扱いはどうしたもんかということで頓挫しました。

日常LISP (3) 常用シェルをCLISPに その2

| 11:41 | 日常LISP (3) 常用シェルをCLISPに その2 - わだばLisperになる を含むブックマーク はてなブックマーク - 日常LISP (3) 常用シェルをCLISPに その2 - わだばLisperになる

シェルをCLISPにしてみる実験の経過報告。

  • まだ全部をCLISPにする、というのは辛い
  • パイプの問題をどうするか
  • プログラミングが簡単にできるので、部分的には、既存のシェルより楽になるところもある
  • やっぱりEmacsのシェルモードから入力すると楽
  • 履歴機能をもう少し充実させたい

といったところです。

改善点は、

  • 括弧/ダブルクォートの対を簡単に入力できるようにした
  • ユーティリティ関数を定義している

というところです。

括弧の入力支援については、

.inputrcに

"\eL": "()\C-b"
"\e\"": "\"\"\C-b"

というようなものを追加しました。

m-"とで、""が入力され、m-sh-Lで()が入力されます。括弧の方はちょっとイレギュラーですが、"("が遠いのでこういう風にしてみました。

また、Emacsのシェルモードで使うのに便利なように、

;; Emacs
(define-key shell-mode-map [(meta ?c)]
  (defun insert-\#\[\] ()
    (interactive)
    (insert "#[]")
    (backward-char)))

こんなのも定義してみました。

ユーティリティ関数については、

;; swank起動
(defun swank (&key (lisp :sbcl))
  (case lisp
    (otherwise #[/lisp/swank-sbcl-1.0.38])))

;; capistranoでデプロイ
(defun cap-staging (name)
  (prog1 (run-program "/home/foo/bin/cap-staging" ;; 既存のシェルスクリプト
                      :arguments (list (string-downcase name)))
         (princ #\Bell)
         (run-program "mpg123" :ARGUMENTS '("/home/foo/sounds/eudora-sound.mp3"))
         (run-program "firefox" :ARGUMENTS (list (case name
                                                   (:project-foo "/var/tmp/deployfinish.html")
                                                   (otherwise "http://unicodesnowmanforyou.com/"))))))

;; ssh
(defun foo ()
  #[ssh foo])

;; IE6マシンに接続
(defun ie6 ()
  #[xvncviewer 192.168.1.6])

という非常にどうでも良いものばかりをずらずら定義していますが、割と便利です。

関数を定義するとファイルに書き出されるようにするともっと便利かもしれません。対話環境については、Interlispの環境がヒントになる気がしたのでちょっと真似してみたいと思っています。

ゲスト



トラックバック - http://cadr.g.hatena.ne.jp/g000001/20101229