2016-12-05 17 views
0

defmacrohttp://clhs.lisp.se/Body/m_defmac.htmで文書化されていますが、実際に起こったことについては完全にはっきりしません。Common Lispでのマクロ展開の規則を規定している規則

  • ストレートトップレベルのコードのみが先に定義されたマクロや機能を呼び出すことができますCLISPを用いた実験により、私は、次の(トップレベルで定義されたすべてのマクロと関数を仮定して)を発見しました。

  • マクロまたは関数内のコード、またはマクロによって生成されたコードは、後で定義するもの(相互再帰をサポートする必要があると予想される)を含む、任意の関数を呼び出すことができます。

  • マクロ内のコードは、最初のマクロの呼び出し元サイトより前に定義されたマクロのみを呼び出すことができます。

  • マクロによって生成されるコードは、後で定義されるマクロを呼び出すことができます。

Clispが仕様に準拠しているかどうか、または実装上のバリエーションはありますか?

正確な意図されたルールセットとその背後にある論理的根拠はどこに文書化されていますか?

+1

http://clhs.lisp.se/Body/03_abab.htm –

+2

マクロの詳しい扱いについては、http://www.paulgraham.com/onlisp.htmlを参照してください。 そこから本をダウンロードできます。 –

答えて

2

マクロ拡張について質問していますが、まず関数の処理方法を明確にしたいと思います。

呼び出しと定義が実際にどのように起こるかに注意してください。 2つ目のポイントでは、関数内のコードが後で定義される関数を呼び出すことができるとします。これは厳密には当てはまらない。

C++のような言語では、関数を宣言して定義してから、アプリケーションをコンパイルします。インライン展開、テンプレート、ラムダ、その他の魔法を無視する...関数をコンパイルするときに、その関数が使用する他のすべての関数の宣言が存在する必要があります。リンク時にコンパイルされた定義が存在する必要があります実行を開始します。プログラムの実行が開始されると、すべての機能がすでに完全に準備され、呼び出す準備ができています。

Lispでは、物事が異なっています。今のところコンパイルを無視してください。ちょうど解釈された環境について考えてみましょう。実行した場合:

;; time 1 
    (defun a() (b)) 
;; time 2 
    (defun b() 123) 
;; time 3 
    (a) 

時刻1にプログラムに機能がありません。

最初のdefunは、機能(lambda() (b))を作成し、それをシンボルaに関連付けます。この関数には、bというシンボルへの参照が含まれていますが、bが呼び出されていない現時点ではです。 aaが呼び出されたときにのみbを呼び出します。

したがって、時刻2では、プログラムにはシンボルaに関連付けられた1つの機能がありますが、まだ実行されていません。

第2のdefunは、機能(lambda() 123)を作成し、それをシンボルbに関連付けます。

時刻3では、プログラムには記号abに関連付けられた2つの機能がありますが、いずれもまだ呼び出されていません。

今、あなたはaを呼び出します。その実行中には、シンボルbに関連する機能のために、は、このような機能はすでに時間この時点で存在することを発見し、それを呼び出して見えます。 bが実行され、戻り123

のは、より多くのコードを追加してみましょう: ;;時間4 関数定義のB()456) ;;時刻5 (a)

時間が経過すると、新しいdefunが456を返す関数を作成し、シンボルbに関連付けます。これはbは、123を返す関数に保持した後、ゴミが収集される(またはごみを取り出すことがないものは何でも実装)の参照を置き換えます。

aを呼び出す(またはより正確に、シンボルaの機能属性によって参照ラムダ)は、今、代わりに、我々が最初に書かれていた場合は456

を返す関数の呼び出しになります。我々はaを呼び出す時間2の後に、それはシンボルbに関連付けられている機能を見つけることができないので、それが失敗するため

;; time 1 
    (defun a() (b)) 
;; time 2 
    (a) 
;; time 3 
    (defun b() 123) 

...これはは、働いていないでしょう。今

からcompileeval-when、最適化およびその他の魔法は、私が上記した内容と異なるファンキーなもののすべての種類を行うが、あなたが最初にそれより高度なものを心配する前に、これらの基本の理解していることを確認することができます。

  1. 関数は、defunが呼び出された時点で作成されます。 (インタプリタはファイル内を先読みしません)
  2. シンボルの属性の1つは、関数への参照です。 (関数自体に実際に名前はありません)
  3. 複数のシンボルは同じ関数を参照できます。シンボルbは時間aによって関連機能を有している限りは、と呼ばれているとしての機能b(口語的に言えば)を呼び出す関数aの定義((setf (symbol-function 'd) (symbol-function 'b))
  4. 、OKです。 (defun ning aの時点では必要ありません)
  5. 記号は、異なる時刻に異なる機能を参照することができます。これは、そのシンボルを「呼び出す」関数に影響します。マクロため

のルールが異なる(その展開には時間を「読み」の後に静的です)ですが、原則の多くは(Lispがそれらを見つけるために、「ファイルに先読み」しません)同じまま。 Lispプログラムは、あなたが慣れ親しんでいるかもしれない大部分の(より少ない;-))言語よりはるかに動的で実行時間が長いことを理解してください。理解する何かのときには、Lispプログラムの実行中にとなり、マクロ展開を支配するルールが意味を持ち始めます。