alet
と言うアナフォリックマクロも紹介されています。定義は以下の通り。
CL-USER> (defmacro alet (letargs &body body)
`(let ((this) ,@letargs)
(setq this ,@(last body))
,@(butlast body)
(lambda (&rest params)
(apply this params))))
ALET
CL-USER>
alet
の動作例はalambda
と組み合わされたなかなか複雑なものが紹介されています。
CL-USER> (alet ((acc 0))
(alambda (n)
(if (eq n 'invert)
(setq this
(lambda (n)
(if (eq n 'invert)
(setq this #'self)
(decf acc n))))
(incf acc n))))
#<CLOSURE (LAMBDA (&REST PARAMS)) {B40D5D5}>
CL-USER>
これはなかなか凄い例です(笑)。
alet
で指示する代名詞this
の中身をsetq
で置き換えてる。実行例は次の通りです。
CL-USER> (setf (symbol-function 'alet-test) *)
#<CLOSURE (LAMBDA (&REST PARAMS)) {BC6900D}>
CL-USER> (alet-test 10)
10
CL-USER>
シンボル
invert
を渡すとalet-test
の挙動が変わります。
CL-USER> (alet-test 'invert)
#<CLOSURE (LAMBDA (N)) {B02F49D}>
CL-USER> (alet-test 3)
7
CL-USER>
つまり、
alet
内のthis
の中身が置き換えられています。
CL-USER> (alet-test 'invert)
#<CLOSURE (LABELS SELF) {BC6D605}>
CL-USER> (alet-test 5)
12
CL-USER>
面白いですね。代名詞を使って丸ごと関数の挙動を変えるとは……。
では、R5RS Schemeで同様のマクロを記述するにはどうするか?
前回見た通り、衛生的マクロではアナフォリックマクロは直接書けません。従って、ここでも外部から束縛すべきシンボルを与えるPseudoなアナフォリックマクロに改造してみます。
(define-syntax alet
(syntax-rules ()
((_ this ((letarg letvar) ...) body ... last)
(let ((this #f) (letarg letvar) ...)
(set! this last)
body ...
(lambda params
(apply this params))))))
割に個人的な感想では、LET OVER LAMBDA Edition 1.0ではCLの
let
らしい乱暴な使い方をしています。CLの場合、
let
は束縛されるべきデータ側を省略しても問題ないようにデザインされています。この場合this
がそうなんですけど、Schemeではそれは許されません。従って、仮に与えるデータとして#f
を束縛しておきます。もう一つはSchemeヴァージョンの
lambda
式の引数が括弧無しの場合、与えられた引数はリストになります。ではSchemeインタプリタで動作を確認してみます。
> (define alet-test (alet this ((acc 0))
(alambda self (n)
(cond ((eq? n 'invert)
(set! this
(lambda (n)
(cond ((eq? n 'invert)
(set! this self))
(else
(set! acc (- acc n))
acc)))))
(else
(set! acc (+ acc n))
acc)))))
> (alet-test 10)
10
> (alet-test 'invert)
> (alet-test 3)
7
> (alet-test 'invert)
> (alet-test 5)
12
>
上手く動いていますね。
0 件のコメント:
コメントを投稿