LISPをどのようにコンパイルして動的にすることができるのかわかりません。言語がコードを操作、変更、生成できるようにするためには、解釈する必要がありませんか?言語を完全にコンパイルし、それでも動的にすることは可能ですか?それとも私は何かが足りないのですか?コンパイルと動的の両方を可能にするLISPは何をしていますか?
LISPは、言語と実装の幅広いファミリーです。
LISPのコンテキストでの動的は、コードが実行時に一定の柔軟性を持っていることを意味します。たとえば、変更または交換できます。これは、動的に型付けされたと同じではありません。
LISPでのコンパイル
多くの場合、LISP実装には実行時に利用可能なコンパイラがあります。このコンパイラがインクリメンタルの場合、プログラム全体は必要ありませんが、単一のLISPフォームをコンパイルできます。次に、コンパイラはインクリメンタルコンパイルをサポートしていると言います。
ほとんどのLISPコンパイラはJust In Timeコンパイラではないことに注意してください。プログラマーとしてのあなたは、例えば、関数 COMPILE
および COMPILE-FILE
。次に、LISPコードがコンパイルされます。
さらに、コンパイラとインタプリタの両方を備えたほとんどのLISPシステムでは、解釈されたコードとコンパイルされたコードの実行を自由に組み合わせることができます。
Common LISPでは、コンパイルされたコードがどれほど動的であるかをコンパイラーに指示することもできます。 [〜#〜] sbcl [〜#〜] (または他の多くの)のコンパイラのようなより高度なLISPコンパイラは、異なるコードを生成できます。
例
(defun foo (a)
(bar a 3))
上記の関数foo
は関数bar
を呼び出します。
グローバル関数bar
があり、それを再定義する場合、LISPでは通常、新しい関数bar
がfoo
によって呼び出されると予想されます。 foo
を再コンパイルする必要はありません。
GNU CLISP を見てみましょう。 仮想マシンのバイトコードにコンパイルされます。これはネイティブのマシンコードではありませんが、ここでの目的のために読みやすくなっています。
CL-USER 1 > (defun foo (a)
(bar a 3))
FOO
CL-USER 2 > (compile 'foo)
FOO
NIL
NIL
[3]> (disassemble #'foo)
Disassembly of function FOO
(CONST 0) = 3
(CONST 1) = BAR
1 required argument
0 optional arguments
No rest parameter
No keyword parameters
4 byte-code instructions:
0 (LOAD&Push 1)
1 (CONST&Push 0) ; 3
2 (CALL2 1) ; BAR
4 (SKIP&RET 2)
ランタイムルックアップ
したがって、BAR
の呼び出しがランタイムルックアップを実行することがわかります。 symbolBAR
を調べてから、シンボルの関数を呼び出します。したがって、シンボルテーブルはグローバル関数のレジストリとして機能します。
インクリメンタルコンパイラ(実行時に利用可能)と組み合わせたこのランタイムルックアップにより、LISPコードを生成し、コンパイルし、現在のLISPシステムにロードして、LISPプログラムを1つずつ変更することができます。
これは、間接参照を使用して行われます。実行時に、LISPシステムはbar
という名前の現在の関数を検索します。ただし、これはコンパイルや解釈とは何の関係もないことに注意してください。コンパイラがfoo
をコンパイルし、生成されたコードがこのメカニズムを使用する場合、それはdynamicです。したがって、解釈されたコードとコンパイルされたコードの両方でルックアップのオーバーヘッドが発生します。
70年代以降、LISPコミュニティは、コンパイラとインタプリタのセマンティクスを可能な限り類似させることに多大な努力を払ってきました。
Common LISPのような言語を使用すると、コンパイラーはコンパイルされたコードの動的性を低下させることもできます。たとえば、実行時にコードの特定の部分の関数を検索しないことによって。
言語がコードを操作、変更、生成できるようにするためには、解釈する必要がありませんか?
番号。
言語を完全にコンパイルし、それでも動的にすることは可能ですか?
はい。
それとも私は何かが足りないのですか?
はい。
コンパイルと動的の両方を可能にするLISPは何をしていますか?
JavaやPyPyのほとんどの実装と同じように、オンザフライでコンパイルされます。
レイトバウンドであるため、コンパイルと動的を同時に行うことができます。関数と引数のリストを実行し、それに何かを追加してから、再度実行することができます。基本的に、コードの各部分は、関数全体だけでなく実行できます。