7

lispコードのファイルをバイトコードまたは生のアセンブリ(またはそのようなfaslファイル)にコンパイルするときに、読み込みマクロとなることを理解するのに少し問題があります。あるいは、私はそれを理解しているかもしれませんが、分かりません。私はちょうど本当に混乱しています。読み込みマクロでLispコードをコンパイルする

読み取りマクロを使用する場合、ソースを使用する必要はありませんか?

これを行うと、読み込みマクロの機能を構成するソースコードを実行する必要があります。あなたがしない場合、あなたはread-charのようなものを行うことができるときにどのように動作するのですか?

これを行うには、読み込みマクロで前述の変数を使用する場合は、すべてのコードを実行する必要があります。このため、実行時にすべてが狂ってしまいます。

前にコードを実行しないと、その上に定義されたものは利用できなくなります。

読み取りマクロを定義する関数またはコンパイラマクロはどうですか? requireまたはloadファイルがコンパイルされていない限り、彼らはまったく動作しないと思います。しかし、彼らがコンパイルされていれば、それらを使用することはできませんでしたか?

私の推測の一部が正しい場合は、ファイル全体をコンパイルするかどうかによって「マクロに使用できるデータ」と「機能に使用できるマクロ」の違いが大きいことを意味します。後で実行するか、一度に1行ずつファイルを解釈する(つまり、ある表現を読み込み、コンパイルし、評価する)ことができます。

要するに、それ以上のマクロ処理や何もせずに実行できる形式に1行をコンパイルすると、前の行を読み込み、コンパイルして実行する必要があります。

それがに来るように、各ラインを実行することができる場所、それを解釈していない、これらの質問は、Lispのコンパイルに適用されることをもう一度思い出してください。

私のとりとめのため申し訳ありませんが、私は、Lispに新しいですし、どのように詳細をお知りになりたいです働く

答えて

1

マクロ(読み取りマクロを含む)は機能に過ぎず、他のすべての機能と同じように扱われます。関数またはマクロがコンパイルされたら、ソースコードを保持する必要はありません。

多くのLisp実装は、何の解釈も全く行いません。たとえば、デフォルトでSBCLは、evalの場合でも解釈モードに切り替えることなく、コンパイルのみを行います。重要なニュアンスは、Common Lispのコンパイルはインクリメンタルであることです(CやJavaなどの多くのScheme実装や言語では共通の別のコンパイルとは対照的です)。これにより、関数やマクロをコンパイルしてそのまま使用することができます。コンパイル単位。

+0

実際には、SBCLには解釈モードがあり、デフォルトではオフになっています。http://www.sbcl.org/manual/Interpreter.html –

+0

一般的なLispコンパイルはインクリメンタルですが、ファイルのコンパイルはわずかに異なります。 –

+0

@Rainerあなたは精巧にできますか?私はそれに精通していません –

5

これは実際には興味深い質問です.Lispプログラマーの多くは苦労しています。これの主な理由の1つは、すべてがほとんどが「期待どおり」に動作し、Lispのより高度な機能を使い始めるときに、これらのことを本当に考え始めることだけです。

あなたの質問に対する短い答えは、はい、コードが適切にコンパイルされるために、前のコードのいくつかが実行されていなければなりません。いくつかの言葉に注意してください、それが鍵です。小さな事例を作ってみましょう。

(print 'a) 
(print 'b) 
:あなたはこのファイルに COMPILE-FILEを実行する場合は、すでに結果 .faslファイルは、単純に次のコードのコンパイルされたバージョンが含まれます、考え出したとして

(print 'a) 

(defmacro bar (x) `(print ,x)) 

(bar 'b) 

:次の内容のファイルを考えてみましょう

"しかし、" DEFMACROフォームがコンパイル時に実行されたのはなぜですか?PRINTフォームはありませんでしたか?答えはHyperspecのセクション3.2.3で説明されています。

通常、 コンパイルファイルにコンパイルされたファイルに表示されるトップレベルのフォームは、ファイルがコンパイルされたときに結果のコンパイル済みのファイルがロードされ 、およびない場合にのみ評価されます。これは、次の文章が含まれています。ただし、ファイル内の一部のフォームをコンパイル時に評価する必要がある場合は、 です。ファイルの残りの部分を読み込んで正しくコンパイルできるように、 の時刻にコンパイルする必要があります。

フォームが評価されるタイミングを正確に制御するために使用できるフォームがあります。この目的のためにEVAL-WHENを使用します。実際、これはLispコンパイラがDEFMACRO自身を実装する方法とまったく同じです。

(macroexpand '(defmacro bar (x) `(print ,x))) 

明らかに異なるLispの実装が異なってこれを実装しますが、キー重要なことは、それがフォームで定義をラップしていることである:あなたのLispはREPLから次のように入力して、それをどのように実装するかを見ることができます(eval-when (:compile-toplevel :load-toplevel :execute) ...)。これは、ファイルがコンパイルされたときと、ファイルがロードされたときの両方でフォームを評価する必要があることをコンパイラーに通知します。これをしなかった場合、定義されたのと同じファイルでマクロを使用することはできません。ファイルがコンパイルされたときにのみフォームが評価された場合は、ロード後に別のファイルでマクロを使用することはできません。ファイルの

+2

コンパイルされたファイルには2つのprintステートメントだけでなく、マクロ定義も含まれています。 –

+1

コンパイル時と実行時の両方でコンパイラマクロを実行することは、実際に私がコンパイラマクロに期待していたことです。しかし、この(私が見ている)このマクロは、読み込みマクロを扱いません。コンパイラマクロは単純な関数です(実際のオブジェクトをパラメータとして受け取り、理論的には実行時まで展開を遅らせることができます)が、読み込みマクロはテキストに依存します。どのように機能するのですか? –

+0

@SethCarnegieコンパイルはインクリメンタルです。各フォームは、読み込まれると処理されます(この文脈で処理されるとは、コンパイルされたものか評価されたものか、あるいはその両方です)。これは、最初のフォームが読者の動作を変更できることを意味し、この新しい動作は、その後のフォームが読み込まれるときに影響を与えます。 –

4

コンパイルは、Common Lispの中で定義されていますCLHS Section 3.2.3 File Compilation

コンパイル中:読み込みマクロを使用してフォームを利用するために、あなたはそれがコンパイラで使用可能なマクロの実装を読んで確認する必要があります。

通常、このような依存関係は、システムのさまざまなファイル間の依存関係(プロジェクトなど)が記述されているdefsystem機能で処理されます。特定のファイルをコンパイルするためには、コンパイルされたLispに別のファイル(コンパイルされたバージョンが望ましい)をロードする必要があります。

ここで、読み込みマクロを定義し、同じファイル内に表記法を使用してフォームを作成する場合は、読み込みマクロとその実装についてコンパイラが認識していることを確認する必要があります。ファイルコンパイラにはコンパイル環境があります。デフォルトでは、同じファイルのコンパイル済み関数をこの環境にロードしません。

コンパイラがファイル内の特定のコードを認識できるようにするため、Common LispはEVAL-WHENを提供します。

はさんが読んマクロの例を見てみましょう:

(set-syntax-from-char #\] #\)) 

(defun reader-example (stream char) 
    (declare (ignore char)) 
    (let ((class (read stream t nil t)) 
     (args (read-delimited-list #\] stream t))) 
    (apply #'make-instance 
      class 
      args))) 

(set-macro-character #\[ 'reader-example) 

(defclass example() 
    ((name :initarg :name))) 

(defvar *examples* 
    (list [example :name e1] 
     [example :name e2] 
     [example :name e3])) 

あなたは上記のソースをロードする場合、すべてが正常です。しかし、ファイルコンパイラを使用すると、最初にロードすることなくコンパイルされません。例えば、ファイルコンパイラは、パス名で関数COMPILE-FILEを呼び出すことによって呼び出されます。今ファイルのコンパイル

:上記

(set-syntax-from-char #\] #\)) 

は、コンパイル時に実行されることはありません。新しい構文の変更はコンパイル時には利用できません。

(defun reader-example (stream char) 
    (declare (ignore char)) 
    (let ((class (read stream t nil t)) 
     (args (read-delimited-list #\] stream t))) 
    (apply #'make-instance 
      class 
      args))) 

上記の関数はコンパイルされますが、ロードされません。この実装は、後の手順でコンパイラで使用することはできません。

(set-macro-character #\[ 'reader-example) 

上記のフォームも実行されません。コードが生成されます。

(defclass example() 
    ((name :initarg :name))) 

コンパイラはクラスを書き留めますが、後でそれをインスタンス化することはできません。

(defvar *examples* 
    (list [example :name e1] 
     [example :name e2] 
     [example :name e3])) 

上記のコードは、読み込みマクロがコンパイル時に使用できないため、前に読み込まれていない限り、エラーをトリガーします。

  • は別のファイルに読み取りマクロの実装を入れて、それがコンパイルされ、リードマクロを使用するすべてのファイルの前にロードされていることを確認してください。

    は現在、2つの簡単な解決策があります。

  • は、コンパイル時に効果を持っている必要があり、コードの周りにEVAL-WHENを置く:

例:

(EVAL-WHEN (:compile-toplevel :load-toplevel :execute) 
    (do-something-also-at-compile-time)) 

上記は、コンパイラによって見ても、その後に実行されます。コンパイル時に、コードに必要なすべての定義があることを確認する必要があります。

言うまでもなく、このようなコンパイルの依存性をできるだけ減らすことは良いスタイルです。通常は、必要な機能を別のファイルに置き、このファイルがコンパイルされ、それを使用するファイルをコンパイルする前にコンパイルLispにロードされていることを確認してください。

関連する問題