マクロの多くの例は、CL の with-open-file など、ラムダを非表示にすることに関するものです。特に PLT スキームで、マクロのより風変わりな使い方を探しています。マクロの使用と関数の使用のどちらを検討するかについて、感触をつかみたいと思います。
10 に答える
新しい制御構造と新しいバインディング構造を実装するには、マクロが必要です。
したがって、 http://planet.plt-scheme.orgでこれらの種類の構成を探してください。PLaneT では、ドキュメントとコードの両方を参照します。
新しい制御構造の例:
http://planet.plt-scheme.org/package-source/soegaard/control.plt/2/0/planet-docs/manual/index.html
新しい結合形式の例を見つけるには、「with-」で始まるマクロを探してください。PLaneT の math.plt にも役立つ例が 1 つあります。
; Within a (with-modulus n form1 ...) the return values of
; the arithmetival operations +, -, * and ^ are automatically
; reduced modulo n. Furthermore (mod x)=(modulo x n) and
; (inv x)=(inverse x n).
; Example: (with-modulus 3 (^ 2 4)) ==> 1
(define-syntax (with-modulus stx)
(syntax-case stx ()
[(with-modulus e form ...)
(with-syntax ([+ (datum->syntax-object (syntax with-modulus) '+)]
[- (datum->syntax-object (syntax with-modulus) '-)]
[* (datum->syntax-object (syntax with-modulus) '*)]
[^ (datum->syntax-object (syntax with-modulus) '^)]
[mod (datum->syntax-object (syntax with-modulus) 'mod)]
[inv (datum->syntax-object (syntax with-modulus) 'inv)])
(syntax (let* ([n e]
[mod (lambda (x) (modulo x n))]
[inv (lambda (x) (inverse x n))]
[+ (compose mod +)]
[- (compose mod -)]
[* (compose mod *)]
[square (lambda (x) (* x x))]
[^ (rec ^ (lambda (a b)
(cond
[(= b 0) 1]
[(even? b) (square (^ a (/ b 2)))]
[else (* a (^ a (sub1 b)))])))])
form ...)))]))
define-syntax
より良いラムダ構文などの小さなことだけに、Scheme マクロ ( ) を使用します。
(define-syntax [: x]
(syntax-case x ()
([src-: e es ...]
(syntax-case (datum->syntax-object #'src-: '_) ()
(_ #'(lambda (_) (e es ...)))))))
あなたが書くことができます
[: / _ 2] ; <-- much better than (lambda (x) (/ x 2))
Dan Friedman は、マクロを使用した驚異的な OO の実装を行っています: http://www.cs.indiana.edu/~dfried/ooo.pdf
しかし正直なところ、私が定義したすべての便利なマクロは、 Paul Graham の On Lispdefmacro
から盗んだものであり、一般に( define-macro
PLT スキームで)より簡単に記述できます。たとえば、aif
はかなり醜いdefine-syntax
です。
(define-syntax (aif x)
(syntax-case x ()
[(src-aif test then else)
(syntax-case (datum->syntax-object (syntax src-aif) '_) ()
[_ (syntax (let ([_ test]) (if (and _ (not (null? _))) then else)))])]))
define-syntax
変数をキャプチャできないことを喜んでいる、非常に単純なマクロでのみ使いやすいという点で奇妙です。非常に複雑なマクロ DSL では、変数を簡単に取得できないことを喜んでいます。最初のケースでは、コードについて深く考えずにコードを書きたいと考えています。2 番目のケースでは、DSL について十分に考えており、不可解なバグを避けるために、Scheme ではないsyntax-rules
/言語でコードの一部を記述しても構わないと思っています。 syntax-case
.
しかし、私はSchemeではマクロをあまり使いません。Idiomatic Scheme は非常に機能的であるため、関数型プログラムを作成してからいくつかのラムダを非表示にしたい場合がよくあります。私は関数型の列車に乗りましたが、怠惰な言語やラムダの優れた構文を使用している場合でも、それは必要ないため、純粋に関数型のスタイルではマクロはそれほど役に立たないと今では信じています。
だから私はPractical Common LispとOn Lispをお勧めします。defmacro
PLT スキームを使用する場合、それらのマクロのほとんどは で動作すると思いますdefine-macro
。または、Common Lisp を使用してください。
最後の質問に答え始めます。関数の代わりにマクロを使用する場合。マクロは関数ができないことを行い、関数はマクロができないことを行うので、それらを混在させるのは難しいでしょうが、より深く見ていきましょう。
引数を評価する場合は関数を使用し、引数を評価しない場合はマクロを使用します。それはあまり役に立ちませんよね?何かを別の方法で書きたいとき、パターンを見て抽象化したいときにマクロを使用します。たとえば、foo の異なる値に対して foo-create、foo-process、foo-destroy という 3 つの関数を定義し、foo だけを変更する同様の本体を定義します。パターンはありますが、機能としてはレベルが高すぎるので、マクロを作成します。
私の謙虚な経験では、Scheme のマクロは、Common Lisp やClojureのような他の Lisp と同じように使用されます。これはおそらく、衛生的なマクロがそれほど良い考えではないことの証明だと思います。ここでは、その理由について Paul Graham に同意しません。汚い (非衛生的な) ことをしたい場合があるからではなく、衛生的なマクロが複雑または複雑になってしまうからです。
PeterSeibelによるPracticalCommonLispには、マクロの優れた入門書があります。ポール・グレアムによるLispについては、より複雑な例の良い情報源かもしれません。また、たとえばCommonLispの組み込みマクロも見てください。
Automata via Macrosの論文では、Scheme でマクロを介して有限状態マシンを実装する関数型プログラミングの真珠を紹介しています。
本The Reasoned Schemerは、本で使用されている論理プログラミング言語である miniKanren の完全なマクロベースの実装で終わります。本稿では、ミニ観念とその実装について、本よりも形式的かつ簡潔に説明します。
curry
手のひらでたくさんの計画をしていたとき、私はマクロを持っていました。とても便利でした。
Schemeマクロを使用すると、元の言語の作成者が自分自身に含めなかった機能を追加できます。それがマクロの背後にある哲学全体です。
小さな例を次に示します。PLTSchemeは、スライドショーと呼ばれるプレゼンテーションを作成するための言語を提供します。マクロを使用してスライド番号をスライドに関連付け、より簡単に管理できるようにしました。
中置構文を提供するマクロを作成しました。あまりにも空想的ではありません。優先しません。私は通常、接頭辞の構文に問題はありませんが、< と > には中置を好みます。
変装したラムダ形式ではない、より高度なマクロの例は Common Lisp のwith-slotsマクロで、オブジェクト スロット アクセスを通常の変数アクセスのように見せます:
(with-slots (state door) car
(when (eq state :stopped)
(setq state :driving-around)
(setq door :closed)))
これは、スロット値をローカル変数にバインドしてそれらにアクセスすることと同じではないことに注意してください。with-slotsを使用すると、SETQ を介してスロットを変更し、外部の変更をすぐに確認できます。
手続きが不十分な場合に使用します。