Posted 2018-12-17 00:05:13 GMT
《 Lisp SETF Advent Calendar 2018 17日目 》
今回は、処理系拡張のsetf
のうち制御構造の拡張を眺めてみたいと思います。
setf
とはCommon Lispの処理系には、setf
の拡張が許されていますが、殆ど拡張は入れてない処理系から突飛なものを入れている処理系まで様々です。
CLISPに結構拡張が入っているので、今回はCLISPを中心に眺めてみます。
setf
でif
が使えるのですが、そこそこ便利かもしれません。
if
を使ったマクロ展開がされてもsetf
の場所として有効なので、言語コアでsetf
展開が可能だと、かなり拡張されることになります。
(let ((x 0)
(y 1))
(incf (if (< x y) x y))
(list x y))
→ (1 1)
or
はif
に展開されるので、直接定義されていなくても(setf or)が使える(let ((x nil)
(y 1))
(incf (or x y))
(list x y))
展開はこんな感じです
(let* ((#:cond-29533 (< x y)) (#:new-29534 (+ (if #:cond-29533 x y) 1)))
(if #:cond-29533
(setq x #:new-29534)
(setq y #:new-29534)))
progn
も言語のコアなので、これもsetf
化の底上げになります。
(let ((x 0)
(y 0)
(z 0))
(incf (progn x y z))
(list x y z))
→ (0 0 1)
locally
も言語のコアなので、マクロ展開の結果への適用を考えているのだと思いますが、the
の代わりに使える気もします。
(let ((x 0)
(y 1)
(z 2))
(setf (locally (declare (fixnum x y z ))
(values x y z))
(values 1 1 1))
(list x y z))
→ (1 1 1)
規格では、(setf apply)
が使えるので、ちょっとした変種というところです。
(let ((u (list 0 1 2)))
(incf (funcall #'cadr u))
u)
→ (0 2 2)
ここからはLisp Machine Lispに実装されていたものですが、locative(参照)が扱えるので、かなり妙なことが可能です。
(let ((x 0)
f)
(setf (let ((x x))
(setq f (lambda () x))
x)
42)
(list x (funcall f)))
→ (0 42)
上記の(setf let)
内のx
は外側のx
をシャドウしていますが、末尾でx
変数を返しているので、その参照に42
を代入しています。
スコープの外から代入できてる感じなのがエグいですが、ここまで極端な使い方は想定していなそうではあります。
これはLisp Machine Lispでも有効になっていませんが、コードの断片があるので有効にしてみると、
(let ((x 0)
(y (list 0 1 2)))
(setf (setq x (car y)) 42)
(list x y))
→ (42 (42 1 2))
こんなことを考えていたようです。
setq
の場合は、値の方の参照に代入します。なかなかエグい。
役に立ちそうなものから、面白機能なものまで紹介してみました。
次回は、リスト操作系の拡張を紹介してみたいと思います。
■
HTML generated by 3bmd in LispWorks 7.0.0