私の Lisp に関する知識はごくわずかであることを率直に認めます。しかし、私はその言語に非常に興味があり、近い将来、それを真剣に学び始める予定です. これらの問題に対する私の理解には間違いなく欠陥があるため、明らかに間違っていることを言う場合は、反対票を投じるのではなく、コメントして修正してください。
真のホモイコニックで自己修正可能な言語
Homoiconicity (コードはデータと同じ表現を持つ) と無制限の自己変更 (新しいコードを発行したり、関数ポインターを変更したりするだけでなく、実行中のコードのあらゆる側面を変更できることを意味する無制限の意味) の両方をサポートするプログラミング言語の例を探しています/代議員。)
私がこれまでに見つけた、この基準に適合する例は 3 つしかありません。
- マシンコード。すべてが数字であるホモイコニック。ポインターが含まれているという点で無制限に変更可能であり、そのアドレスがコードまたはデータを保持しているかどうかに関係なく、任意のメモリ アドレスを操作するために使用できます。
- マルボルジ。マシンコードと同じ理由。すべての命令は、実行後に自身を変更します
- DNA。プログラミング言語ではありませんが、それでも興味深いものです。マシンコードと同じ意味での自己変更ではありません。実際の命令とデータが変更された場所。ただし、それは自己複製であり、以前の状態に応じて変異/進化する可能性があります (放射線などの副作用が時々発生します)。とにかく、これは自己修正の間接的な方法です。簡単に言えば、DNA は自己改変することができますが、それは関連する突然変異と共にその全体を複製することによって行われます。DNA の物理的な文字列は「不変」です。
Lisp がこのリストにない理由
Lisp はそのリストに含まれていません。なぜなら、Lisp はほぼホモイコニックであり、制限付きの自己変更しかサポートしていないように思われるからです。次のようなことができます
(+ 1 2 3)
と同じことを行います
(eval '(+ 1 2 3))
最初のバージョンでは(+ 1 2 3)
は生のコードですが、2 番目のバージョンではデータです。このステートメントが真実であると仮定することにより、Lisp はホミニックでさえないと主張することができます。コードは、リスト/ツリー/S 式の両方であるという意味で、データと同じ表現をしています。しかし、これらのリスト/ツリー/S 式のどれがコードで、どれがデータであるかを明示的にマークしなければならないという事実は、結局のところ Lisp はホミニックではないと言っているように思えます。表現は非常に似ていますが、コードまたはデータを扱っているかどうかを実際に言わなければならない細部が異なります。これは決して悪いことではありません (実際、それ以外のことはすべて狂気です) が、Lisp とマシン コードの違いを浮き彫りにしています。マシン コードでは、どの数値が命令で、どの数値がポインターで、どの数値がデータであるかを明示的にマークする必要はありません。
これは、無制限の自己変更に対するさらに強力なケースです。もちろん、コードを表すリストを取得して操作することもできます。たとえば、
'(+ 1 2 3)
に
'(+ 1 4 3)
そして、あなたはそれを実行しますeval
。しかし、これを行うと、コードをコンパイルして実行するだけです。既存のコードを変更するのではなく、新しいコードを発行して実行するだけです。C# は式ツリーを使用してまったく同じことを行うことができますが、形式があまり便利ではありません (これは、独自の AST である Lisp とは対照的に、C# コードがその AST とは異なる表現を持つために発生します)。実際にソース ファイル全体を取得し、実行中にそのソース ファイル全体を変更して、ソース ファイルに加えられた変更がプログラムの動作にリアルタイムで影響を与えることはできますか?
これを行う何らかの方法がない限り、Lisp はホモニックでも自己修正でもありません。(定義に関する議論を先延ばしにするために、Lisp はホモイコニックでもなければ、マシン コードと同じ程度の自己修正でもありません。 )
Lisp Homoiconic/Unrestrictly 自己変更可能にする方法
Lisp をマシンコードのようにホモイコニック/自己修正可能にする 3 つの方法が考えられます。
- 非フォンノイマン アーキテクチャ。誰かが、プログラムの最下位レベルの表現が直接実行できる AST である驚くべき仮想マシンを発明できたら (それ以上のコンパイルは必要ありません)。そのようなマシンでは、AST は実行可能な命令とデータの両方を表します。残念ながら、AST は依然としてコードまたはデータのいずれかでなければならないため、問題は解決されていません。eval 関数が存在しても、これは変わりません。機械語では、コードとデータの間を好きなだけ行ったり来たりできます。一方、eval と Lisp では、リストをデータからコードに「評価」して実行すると、そのリストを再びデータとして取得する方法はありません。実際、そのリストは永久になくなり、その値に置き換えられました。たまたまポインターである重要なものが欠けている可能性があります。
- ラベルを一覧表示します。すべてのリストにも一意のラベルが必要である場合、特定のラベルを持つリストに対して関数を実行することにより、間接的な自己変更を行うことができます。継続と組み合わせることで、最終的にマシンコードと同じ意味で自己修正コードが可能になります。ラベルは、マシン コードのメモリ アドレスと同等です。例として、AST の最上位ノードに「main」というラベルが付いている Lisp プログラムを考えてみましょう。次に、main 内で、ラベル、整数、アトムを受け取る関数を実行し、アトムを、関数に指定されたインデックスに一致するラベルを使用してリストにコピーできます。次に、メインで現在の継続を呼び出します。ほら、自己修正コード。
- Lisp マクロ。私は Lisp マクロを理解するのに時間をかけたことがありません。実際、それらは私が考えていることを正確に実行する可能性があります。
ポイント 1. と 2. を組み合わせると、完全に自己変更的な Lisp が生成されます。説明されている魔法の Lisp マシンを作成できるという条件で。2. 単独で自己変更 Lisp を作成できますが、フォン ノイマン アーキテクチャでの実装は非常に非効率的です。
質問
- マシンコード、DNA、malbolge 以外に、完全自己改変が可能でホモイコニックな言語はありますか?
- (上記のテキストで tl;dr を実行した場合は、わざわざ回答しないでください) . Lispは本当にホモイコニック+自己修正ですか? もしあなたがそうおっしゃるなら、私の議論のどこで私が道に迷ったのかを正確に引用していただけますか?
付録
制限のない自己修正を伴うがホモニシティを持たない言語
- 組み立て。コードは数字ではなく単語を使用するため、同義性は失われますが、メモリに対する完全な制御を保持し、無制限の自己変更を可能にするポインタがまだあります。
- 生のポインターを使用するすべての言語。例: C/C++/Objective C。アセンブリと同じ引数
- 仮想ポインターを含む JIT 言語。たとえば、安全でないコンテキストで実行されている C#/.net などです。Assembly と同じ引数。
何らかの形で関連/興味深い可能性のあるその他の概念と言語: Lisp、Ruby、Snobol、Forth とコンパイル時のメタプログラミング、Smalltalk とそのリフレクション、すべてが関数であるというプロパティを持つ型指定されていないラムダ計算 (これは、ラムダ計算を直接実行するマシンを発明できれば、ラムダ計算はホモイコニックであり、フォン ノイマンのマシン コードは、そのマシンで実行するとそうではありません.[そして、ゲーデルの定理は実行可能になるでしょう.ハハ、恐ろしい考え :P])