mokeheheのScheme日記

ツッコミ、添削大歓迎です。いろいろ教えてください。

2008-01-04

[] Practical Common Lisp第8章 マクロ:自分のを定義する

http://gigamonkeys.com/book/macros-defining-your-own.html

  • マクロ理解への障壁は、皮肉にもそれが言語によくマッチしてるからだ。マクロはちょっと変わった関数にしか見えない。
  • しかしマクロ関数とは異なるレベルであり、異なる種類の抽象化をもたらす
Macの話:単なる話

マクロがないLispを使ってた会社にMac氏が現れて、記述の置き換えの仕事をしていた、そしてそれはコンパイラに組み込まれましたとさ

マクロ展開時 vs 実行時
defmacro

マクロを書くステップ:

  1. マクロのサンプル呼び出し、展開されるべきコードを書く
  2. サンプルコールの引数から手で展開したコードを書く
  3. マクロの抽象化に漏れがないようにする
サンプルマクロ:do-primes
  • 素数でループする、do-primes を書く
  • loop を do で書くと
(defun primep (number)
  (when (> number 1)
    (do ((fac 2 (1+ fac)))
        ((> fac (isqrt number)) t)
      (if (zerop (mod number fac))
          (return-from primep nil)))))

(defun next-prime (number)
  (do ((n number (1+ n)))
      ((primep n) n)
    ))
  • いいかげんloopからdoへの書き換えはメンドイな。loop マクロが書ければいいんだけど。
マクロパラメータ
展開の生成
  • バッククォートを使えば、カンマやカンマアットでクォートを打ち消すことができる
    • カンマアットだとリストをその場に展開する
  • 展開をチェックするには、実行結果を見るか
  • macro-expand-1 を使って展開されたコードを見るか
    • macro-expand-1関数なので、クォートする必要がある
漏れを塞ぐ
  • Joel Spolskyの"The Law of Leaky Abstractions"の中で、抽象化をだいなしにしてしまう、詳細が漏れる抽象化のことを漏れやすい抽象化という用語を作り出した。
  • マクロも抽象化の方法だから、漏れのないようにしないとね
  • 現在の定義は漏れる可能性が3つある
(defmacro do-primes ((var start end) &body body)
  `(do ((ending-value ,end)
        (,var (next-prime ,start) (next-prime (1+ ,var))))
       ((> ,var ending-value))
     ,@body))
      • 残念なことに、この修正がマクロ抽象化に新たに2つの漏れを発生させる
    • 2. start と end の評価順が違う
(defmacro do-primes ((var start end) &body body)
  `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
        (ending-value ,end))
       ((> ,var ending-value))
     ,@body))
    • 3. "ending-value" という変数名を使ってしまっている
      • package を使って解決もできるが、もっといい方法がある
      • getsym 関数は呼び出されるたびにユニークなシンボルを返す、これを使って
(defmacro do-primes ((var start end) &body body)
  (let ((ending-value-name (gensym)))
    `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
          (,ending-value-name ,end))
         ((> ,var ,ending-value-name))
       ,@body)))

マクロ作成時には以下のルールに従え:

  • マクロ呼び出しのパラメータ順に評価しろ
  • 部分式の評価は一度だけにしろ
  • 展開時に使われる変数にはgetsymを使え

リンク:

マクロを書くマクロ
  • マクロを書くときは、マクロ展開時に使う変数を導入するために、大抵 let で始まる。ならこの定型処理をマクロ化しよう!
(defmacro with-gensyms ((&rest names) &body body)
  `(let ,(loop for n in names collect `(,n (gensym)))
     ,@body))
他の古くからあるマクロを書くマクロ:once-only
(defmacro once-only ((&rest names) &body body)
  (let ((gensyms (loop for n in names collect (gensym))))
    `(let (,@(loop for g in gensyms collect `(,g (gensym))))
      `(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n)))
        ,(let (,@(loop for n in names for g in gensyms collect `(,n ,g)))
           ,@body)))))
  • なにをやってるのかよくわからん
    • 一番外側の (let ...) で、once-only に与えられたシンボル用にgensymをして、
    • その内側の `(let ...) で1回だけ評価した値を格納するためのシンボルを作成、
    • その内側で once-only に与えられたシンボルを評価してテンポラリに格納
    • その内側で once-only に与えられたシンボルと同じ名前にバインドしなおす
  • 一番最初の (let ((gensyms ... で、gensyms というシンボルを使ってしまっているため、もしonce-onlyの外側でgensymsを使ってたら動作がおかしくなると思うのだが…。

g000001g0000012008/01/05 02:15はじめまして、自分もPCLを読んでみている者です。
自分はxyzzyはちょっと使ったことがある程度なのですが、
loopマクロはxyzzyにもlispディレクトリ以下に隠れてるらしいというのをどっかでみた記憶があったのを思い出して確認ちょっと確認してみました。
xyzzyだと(require "cmu_loop")で使えるようです。
cmuclから移植したのでこんな名前なのかもしれないですね。

mokehehemokehehe2008/01/05 07:19g000001さん、はじめまして。ブログ読ませてもらってます (^^;;
お書きのとおり、(require "cmu_loop") で本の中の loop が実行できました!ありがとうございます。

PCL を読むまで loop マクロというものを知りませんでした。
マクロだけでこんなムチャクチャできちゃうのはすごいですね。
loop は読みやすくていいですね。自分で書こうとは思いませんが (^^;;

DebraDebra2011/04/11 01:34u9f3XY Touchdown! That's a really cool way of putting it!

lytiwalytiwa2011/04/12 04:50ZECap3 <a href="http://srzjytyhymno.com/">srzjytyhymno</a>

yoshwamtyoshwamt2011/04/13 06:57UsHCYm , [url=http://fpvhnjgcsbyr.com/]fpvhnjgcsbyr[/url], [link=http://dujzatdeswnu.com/]dujzatdeswnu[/link], http://bgdvkybuwzjd.com/

rkmntuurkmntuu2011/04/13 06:57wGmkbz , [url=http://pidabylfpbmq.com/]pidabylfpbmq[/url], [link=http://xekbosghhjxg.com/]xekbosghhjxg[/link], http://hyfwxibcjdmv.com/

ckazhukckazhuk2011/04/23 03:24lcc4uc <a href="http://euvnhygtrqom.com/">euvnhygtrqom</a>

fvqnydtirfvqnydtir2011/04/24 11:46kkgpYy , [url=http://zdkboicpubmh.com/]zdkboicpubmh[/url], [link=http://zeliqhxdzinl.com/]zeliqhxdzinl[/link], http://ahsqzzzpbfdq.com/

トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080104