自己再生コード (実行時にソースを出力する) を記述するにはどうすればよいですか?
-
01-07-2019 - |
質問
私は、実行時に独自のソースを出力するプログラムを作成する必要がある、この問題に対する C/C++ ベースの解決策を数多く見てきました。
いくつかの解決策 --
http://www.cprogramming.com/challenges/solutions/self_print.html
ネット上にはさらに多くのソリューションがあり、それぞれが異なります。このような問題にどのようにアプローチすればよいのか、それを解決する人の頭の中はどうなっているのだろうか。この問題についての洞察を教えてください...Perl、php、ruby などのインタープリタ型言語での解決策は簡単かもしれませんが...コンパイル言語でどのように設計すればよいのか知りたいのですが...
解決
不正行為¹ を除けば、コンパイル言語とインタプリタ言語の間に違いはありません。
クインへの一般的なアプローチは非常に簡単です。まず、プログラムがどのようなものであっても、ある時点で何かを出力する必要があります。
print ...
しかし、何を印刷すればよいのでしょうか?自体。したがって、「print」コマンドを出力する必要があります。
print "print ..."
次に何を印刷するべきでしょうか?さて、その間にプログラムが大きくなったので、「print」で始まる文字列も出力する必要があります。
print "print \"print ...\""
プログラムが再び大きくなったので、出力するものがまた増えました。
print "print \"print \\\"...\\\"\""
等々。コードが追加されるたびに、出力するコードが増えます。このアプローチはどこにも行きませんが、興味深いパターンを明らかにしています。文字列「print \"」が何度も繰り返されます。繰り返しの部分を変数に入れるといいでしょう:
a = "print \"" print a
ただし、プログラムが変更されたばかりなので、次のように調整する必要があります。
a = "a = ...\nprint a" print a
「...」を埋めようとすると、以前と同じ問題に遭遇します。最終的には次のようなものを書きたいと思います。
a = "a = " + (quoted contents of a) + "\nprint a" print a
しかし、それは不可能です。なぜなら、私たちがそのような機能を持っていたとしても quoted()
引用するために、私たちが定義する問題がまだあります a
それ自体に関して:
a = "a = " + quoted(a) + "\nprint a" print a
したがって、私たちができる唯一のことは、プレースホルダーを配置することです a
:
a = "a = @\nprint a" print a
それがすべてのトリックです!それ以外のことはもう明らかです。場所ホルダーを引用された内容に置き換えるだけです a
:
a = "a = @\nprint a" print a.replace("@", quoted(a))
コードを変更したため、文字列を調整する必要があります。
a = "a = @\nprint a.replace(\"@\", quoted(a))" print a.replace("@", quoted(a))
以上です!すべての言語のすべてのクインはそのように機能します(不正行為を除く)。
さて、場所ホルダーの最初の発生のみを置き換えることを確認する必要があります。また、2番目のPlace Holderを使用する場合は、文字列を引用する必要がないことを避けることができます。
しかし、それらは小さな問題であり、簡単に解決できます。事実であれば、その実現は、 quoted()
そして replace()
さまざまなクインが実際に異なる唯一の詳細です。
¹ プログラムにソースファイルを読み取らせることによって
他のヒント
Quine を記述するには、いくつかの異なる戦略があります。明らかな方法は、コードを開いて出力するコードを記述するだけです。しかし、より興味深いものには、多くの言語にある %s スタイルの printf 機能など、自己埋め込みを可能にする言語機能が含まれています。最終的に埋め込みリクエストに解決されるように、何かを埋め込む方法を考え出す必要があります。回文と同じように、多くの試行錯誤が含まれているのではないかと思います。
また、Core Wars ゲームがどのように動作するかを学ぶこともできます。良い例だと思います。
通常のアプローチ (チート * ができない場合) は、ソースを文字列定数でエンコードし、その定数を 2 回出力するものを作成することです。1 回は文字列リテラルとして、もう 1 回はコードとして。それは「コードの行を書くたびに、別のコードを書くためにそれを印刷する必要があります!」問題。
「不正行為」には次のものが含まれます。- 解釈された言語を使用して、単にソースをロードして印刷する-0バイト長いファイル。
楽しみのために、Scheme でこれを思いつきました。以前に発見されたことがわかるまで、5 分間ほど誇りに思っていました。とにかく、Lisp のデータとコードの二重性をより適切に考慮するために、ゲームの「ルール」にわずかな変更が加えられています。プログラムのソースを出力する代わりに、それ自体を返す S 式です。
((lambda (x) (list x `',x)) '(lambda (x) (list x `',x)))
の ウィキペディアにあるもの は同じ概念を持っていますが、引用のためのメカニズムが少し異なります (より冗長です)。でも、私は私の方が好きです。
エンコードについて考え、何かに二重の意味を与えて、いくつかの形式で何かを出力できるようにする方法を考える 1 つのアイデアです。また、この種の問題には制限があり、プログラム出力自体以外のルールがなければ空のプログラムが解決策となるため、制限が難しくなるという注意点もあります。
これに対するかなりの解決策がここにあります。 http://forums.thedailywtf.com/forums/p/5232/147528.aspx
実際にソースコードを読んで印刷してみてはどうでしょうか?全然難しくないよ!!これはphpでの1つです:
<?php
{
header("Content-Type: text/plain");
$f=fopen("5.php","r");
while(!feof($f))
{
echo fgetc($f);
}
fclose($f);
}
?>
Python では次のように書くことができます。
s='c=chr(39);print"s="+c+s+c+";"+s';c=chr(39);print"s="+c+s+c+";"+s
この自己印刷疑似コードからインスピレーションを得たものです。
Print the following line twice, the second time with quotes.
"Print the following line twice, the second time with quotes."
これに興味がある人のために AS3 の例を作成しました。
var program = "var program = @; function main(){trace(program.replace('@',
String.fromCharCode(34) + program + String.fromCharCode(34)))} main()";
function main(){
trace(program.replace('@', String.fromCharCode(34) + program + String.fromCharCode(34)))
}
main()
ルビーでは:
put File.read(_ _ FILE _ _)