Lispマクロ定義についての疑問:なぜ展開形がエラーにならずに正しく動作するのか?

このQ&Aのポイント
  • Lispのマクロ定義において、バッククオート内の`,`がついた要素はバッククオートの影響を受けずに展開されます。
  • そのため、`(setq ,var (1+ ,var))`の展開形は、`(setq 1 (1+ 1))`となります。
  • バッククオート内の`,`は、バッククオートをエスケープするための特別な演算子であり、バッククオートの効果を一時的に無効化する役割を果たします。
回答を見る
  • ベストアンサー

lispのマクロ定義について

例えば (setq x 1) とします。 また引数をインクリメントするマクロincの定義を (defmacro inc (var) `(setq var (1+ var))) とします。 さてここで (inc x) とすると 2 が返ってきます。 この時、マクロが構築する展開形は (setq x (1+ x)) ですよね? では、もしincの定義が (defmacro inc (var) `(setq ,var (1+ ,var))) ならばマクロが構築する展開形はどうなりますか? (inc x)とすると(xの値は1)展開形は (setq 1 (1+ 1)) となりエラーになると思います。 しかしこれはエラーにならず、ちゃんと2が返ってきます。 バッククオートのなかの , がついた要素はバッククオートの影響を受けないはずなのに , を書かない時と同じになるのはおかしいと思います。 なぜこんなことが起きるのでしょうか? 誰か教えてください、お願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.1

どんな環境で試されましたか? >clisp i . . . . I . i i i ooooo o ooooooo ooooo ooooo I I I I I I I I I 8 8 8 8 8 o 8 8 I I \ `+' / I I 8 8 8 8 8 8 I \ `-+-' / I 8 8 8 ooooo 8oooo \ `-__|__-' / 8 8 8 8 8 `--___|___--' 8 o 8 8 o 8 8 | ooooo 8oooooo ooo8ooo ooooo 8 --------+-------- Welcome to GNU CLISP 2.48 (2009-07-28) <http://clisp.cons.org/> Copyright (c) Bruno Haible, Michael Stoll 1992, 1993 Copyright (c) Bruno Haible, Marcus Daniels 1994-1997 Copyright (c) Bruno Haible, Pierpaolo Bernardi, Sam Steingold 1998 Copyright (c) Bruno Haible, Sam Steingold 1999-2000 Copyright (c) Sam Steingold, Bruno Haible 2001-2009 Type :h and hit Enter for context help. [1]> (setq x 1) 1 [2]> (defmacro inc (var) `(setq var (1+ var))) INC [3]> (macroexpand '(inc x)) (SETQ VAR (1+ VAR)) ; T [4]> (inc x) *** - SETQ: variable VAR has no value The following restarts are available: USE-VALUE :R1 Input a value to be used instead of VAR. STORE-VALUE :R2 Input a new value for VAR. ABORT :R3 Abort main loop Break 1 [5]> (defmacro inc (var) `(setq ,var (1+ ,var))) INC Break 1 [5]> (macroexpand '(inc x)) (SETQ X (1+ X)) ; T Break 1 [5]> (inc x) 2

puntero
質問者

補足

xyzzyなのでcommonlispだと思います。 (defmacro inc (var) `(setq var (1+ var))) の定義でやるとエラーになるということでしょうか?

その他の回答 (1)

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.2

マクロとバッククオートの展開に誤解があるようです #1さんの実行例を良く見てください。 (defmacro inc (var) `(setq var (1+ var))) → (macroexpand '(inc x)) →(SETQ VAR (1+ VAR)) varは「var」のまま。 (defmacro inc (var) `(setq ,var (1+ ,var))) → (macroexpand '(inc x)) → (SETQ X (1+ X)) ,varがxに置き換わる。(「xの評価」に置き換わる、ではない) 実行の前に(setq var 1)してあった、とか、 (defmacro inc (x) `(setq x (1+ x))) で定義してたとかはないですか? (inc x) のあとで x だけで評価したらちゃんと変わってますか?

関連するQ&A

  • 引数をつけたマクロ定義とは

    引数つきマクロ宣言って、どういった場面で使うのでしょうか? 例えば #define abc(a) a*a と定義している場合 x=abc(a) のabc(a)がa*aに変わる。 でも、こんなことしたら abc(a)関数を呼び出せなくなるのでは? 要するに、「引数つきのマクロ定義が ある理由などを、説明していただければと思います。

  • lispのwhile

    lispの特殊形式whileを以下のように定義します。 (while 判定条件 本体…) 第一引数である判定条件を評価し真ならば本体を評価しもういちど判定条件を評価、偽ならば本体を評価しない。 この時次のコードでよくわからない部分があります (defmacro image (var list &rest forms) `(let (($list$ ,list) ($r$ nil) (,var nil) ) (while ($list$ (nreverse $r$)) (setq ,var (pop $list$)) (push (progn ,@forms) r) ))) ($list$ (nreverse $r$)) この文は 偽(つまりnil) になりえるのですか? $list$はpopを繰り返すのでループを繰り返せばnilになるでしょう。 しかし (nreverse $r$) はpushを繰り返すのでnilにはなりえないと思います。 また、もしnilになりえたとしても (while (nil nil)) は (while (nil)) と同値なのでしょうか? ちなみにこれはlispの参考書のコードそのままです。 読み進めていくうちにだんだん難しくなってきました。 もうどれだけ考えてもわからないので、気持ち悪い思いはしながらもどんどん先に進んでいます。 こんな勉強法でいいんでしょうか? 不安になります。

  • Lispについてわからないことが(Scheme)

    あるLispの勉強ソフトで、 「関数lを『引数としてxを受け取ると、xの要素の数を返す関数』として定義しなさい。」 という問題があるのですが、私は以下のようにしました。 (define l (lambda (x) (if (= x null?) 0 (+ 1 (l (cdr x)))))) しかしこれだとオーバーフローと表示されて強制終了されてしまいました。 そこで答えをネットで検索したところ以下のものが見つかりました。 (define l (lambda (x) (cond ((null? x) 0) (else (+ 1 (l (cdr x))))))) これが正解なようですが、この2つのリストの違いがわかりません。 初歩的なことですがifの使い方を間違っているんでしょうか?

  • UWSCでの関数定義は?

    只今UWSCにて画像クリックを自動化しようとしているのですが、CHKIMGとBTNを多用するため、関数にて引数に 画像のディレクトリ・検索範囲のx座標1・y座標1・x座標2・y座標2 を入れて呼び出したいと思っています。 説明には  PROCEDURE 関数名( 引数, Var 引数, 引数[], Var 引数[][], 引数=定数, .... )      処理 FEND と載っていたのですが、関数を作成するとメインの動作をしなくなってしまいました。 UWSCではどのように関数の定義と呼び出しをすればいいのでしょうか。 初歩的な質問で申し訳ないのですが、分かる方がいらっしゃったらお願いします。

  • C言語の関数形式マクロの定義・呼び出し時の空白の挿入

    C言語で関数形式マクロを定義するときに、どこに空白(スペース)を入れてもよいのか、教えてください。 関数形式マクロとは、引数付きマクロと言っている本もあるようで、例えば、 #define mymul(x,y) ((x)*(y)) こんなものです。 mymul(a,b)と書いてあったら、((a)*(b))に置き換えますという意図です。 ・#の前 ・#とdefineの間 ・mymulと(x,y)の間 ・(とxの間 ・xと,(カンマ)の間 ・,(カンマ)とyの間 ・yと)の間 ・マクロ定義本体(つまり右側)の 一番目の括弧と二番目の括弧の間 ・二番目の括弧とxの間  以下同様に・・・・・・・・・・・・・ 呼び出す場合も、c=mymul(a,b)と呼び出す場合に、 ・=とmymulの間 ・mymulと( の間 ・( と a の間 ・aと,(カンマ)の間  ・・・・・・ これらのどこに入れてよいのか、 よろしくお願いいたします。 全角で書いてあるところがあります。例としてあげた定義の実益については問わないことにしましょう。

  • maximaでの変数定義

    例えば x=a+b^2-c と定義してから、 x~2+x*a-a*b なんかを展開したい時など、 あらかじめxを定義する方法を知りたいのですが、 教えて下さい。

  • 基礎体温のエクセルソフト(マクロ)がVistaで動かない

    パソコンを買い替えてVistaに変わったところ、XPで動いていたエクセルソフトが動かなくなりました。 マクロを実行するとエラーがでてしまいます。 ソフト「操作性抜群!基礎体温表作成マクロ」     http://www.vector.co.jp/soft/win95/home/se259668.html エラー内容「実行時エラー'1004':       アプリケーション定義またはオブジェクト定義のエラーです。」 このソフトを使えるようになる方法があれば教えてください。 類似ソフトを探してみても、使いたい要素が揃っているものがなくて・・・。 よろしくお願いします。

  • 条件演算子でのインクリメントの使用

    マクロとして以下のような定義をします。 #define max(x,y)  ((x)>(y)?(x):(y)) この時ある本に以下のような記述がありました。 「マクロでは、max(x++,y)が、((x++)>(y)?(x++):(y))に展開されてインクリメントが2回行われるという副作用が発生します。ここに示す実現は、int型しか扱えないことが欠点とです」 とありましたが、以下のプログラム #include<stdio.h> #include"max.h"←maxマクロの定義 int main(void) { double x=1.1,y=2.0,max; max(x++,y): max=max(x,y); printf("x=%f y=%f\n",x,y); printf("max=%f\n",max); return 0; } をコンパイルすると x=2.100000 y=2.000000 max=2.100000 の結果がでます。これは 1.条件演算子ではインクリメントは1回しかおこなわれない。 2.条件演算子はdouble型でも実現できる ことになります。 この事は「インクリメントが2回行われるという副作用が発生します。ここに示す実現は、int型しか扱えないことが欠点です」という事に反する結果だとおもいますが、どこか間違いがあるのでしょうか。宜しくお願いします。環境としてはRed Hat でviを使っています。

  • マクロでセルのコピー

    シート2のE1の値をシート1のJ3にコピーしよおとマクロで書いたのですがエラーが出てうまくいきません。 Private Sub Worksheet_Deactivate()  Sheet1.Range("J3").Value = Sheet2.Range("E1") End Sub エラー内容は、実行時エラー'1004' アプリケーション定義またはオブジェクト定義のエラーです。 マクロがあまりくはしくないので修正方法がわかりません。よろしくお願いします。

  • マクロ FINDについて

    いつも回答して頂き、とても感謝しています。 マクロを1回実行させた時はすんなりいくのですが、続けて同じマクロを実行させるとFINDの記述の箇所でNothingになり、エラーが発生してしまいます。 引数を『Set 〇〇 = Nothing』でリセットしても駄目でした。しかし、ブックを一度閉じた後、実行すると最初だけはエラーが発生しません。 記述を載せたいのですが、会社で作ったものなので、持ち帰る事もできません。 この説明だけで申し訳ないのですが、対応方法を教えて頂けないでしょうか?宜しくお願い致します。

専門家に質問してみよう