クインを書くためのヒント


30

プログラムのソースコードと同一の出力を生成するプログラムです。このWebサイトでは、通常、適切なクインだけを扱います(執筆時点では、現在の定義は「出力の一部はプログラムの別の部分によってエンコードされています」)。

適切なクイン、またはクインのような特性を持つプログラムを作成するためのアドバイスはありますか?いつものように、各ヒントは異なる答えにあるべきです。

tips  quine 

回答:


14

evalコードをコピーする必要性を減らすために使用します

ほとんどのクインは、コードの2つのコピーを必要とします。1つは実行、もう1つはデータとして。これにより、ソースコードの長さが2倍になり、維持が難しくなり、コンペティションでクインを書いている場合はスコアが悪化します。

2つのコピーをマージすると、1つの情報を2つの目的に使用する必要があります。コードをデータとして扱うことは不可能な場合が多く、通常は不正行為と見なされます。ただし、データをコードとして扱うことは、多くの言語で、通常はと呼ばれるビルトインを使用して行うことができますeval。そのため、クインは基本的に、クインの本体を変数に保存し(そのため、複数回参照できるように)、その変数を評価することで構成されます。

これがどのように機能するかの例を次に示します(この例はPythonで記述されていますが、他の多くの言語でも同様の機能が動作します)。

d='print("d="+repr(d)+"\\neval(d)")'
eval(d)

2
@QPaysTaxes:勝利条件が許す限り、ここでコードを読みやすく保守しやすいものにすることを目指しています。あいにく、それはまだ、その言語に慣れていない人々からのASCIIラインノイズ(またはJellyを使用している場合は通常のラインノイズ)と通常区別できません。

14

文字列のフォーマットを活用する

クインを作成する最も簡単な方法の1つは、文字列を定義し、文字列の書式を設定して文字列をその中に置くことです。

s='s=%r;print s%%s';print s%s


したがって、このPython quineの例では、最初の部分がstringの前にあるものと等しい文字列を宣言し、文字列をs=でフォーマットして挿入できるようにし、%r最後に文字列の後を印刷してフォーマットします。末尾の改行はprint、末尾の改行を出力するためです。

したがって、Pythonでのテンプレートは本当にこれです。

<A>'<A>%r<B>'<B>

より多くのコードで既存のクインを拡張するには:

<more A>s='<more A>s=%r;print s%%s<more B>';print s%s<more B>

9

文字列化された関数

いくつかの言語では、関数オブジェクト(または同等の構成体)はソースコードを暗黙的に格納し、文字列に変換されるとそれを返します。これにより、文字列eval使用せずにコンパクトなクインが可能になります。このような言語の注目すべき例はJavaScriptです。

function f(){console.log(f+"f()")}f()

このコードは、呼び出されたfときに独自のソースコードを出力し、その後に自身を呼び出す関数を定義して呼び出します。複製する必要があるプログラムの唯一の部分は、関数呼び出しf()です。もちろん、関数の本体には、関数が呼び出されたときにも実行される任意の「ペイロード」コードを含めることができます。


同じトリックのよりコンパクトなバージョンは、ゴルフ言語GolfScriptで機能します。

{".~"}.~

CJam

{"_~"}_~

これらの各クインは、最初に(中括弧で囲まれた)匿名コードブロックを定義します。これは、JavaScriptの関数オブジェクトのように動作します。実行でき、文字列化されている場合は、ソースコードを返します。その後、残りのコード(.~GolfScriptまたは_~CJam)がブロックを実行し、そのコピーをスタックに残します。次に、ブロック内のコードは、ブロック外のコードを繰り返す文字列をスタックにプッシュします。インタプリタが終了すると、スタックに残っているすべてを自動的に文字列化して出力します。JavaScriptの例と同様に、これらのコードブロックは、複製することなく、追加コードの任意のペイロードを実行するように作成することもできます。


9

エスケープせずにネストする文字列区切り記号を使用する

多くの場合、クインの作成に関する最も難しい部分の1つは、エスケープ手順です。これはほとんどすべての馬で必要です。問題は、何らかの方法でデータを保存していることであり、データを保存するコードをクインの出力に複製する必要があります。このコードにはエスケープ形式のデータが含まれているため、プログラムにはエスケープされていない形式が表示されるため、再エスケープする必要があります。

エスケープ解除ステップを処理する最も簡単な方法は、データのエスケープされた形式とエスケープされていない形式が、文字列区切り文字の有無のみで異なる場合です。したがって、エスケープは、ストリングの周りにストリング区切り文字の新しいペアを追加するという単純な問題です。残念ながら、文字列区切り文字自体をエスケープせずにデータで表現できる場合にのみ、これは明らかに機能します。

Perlは、このトリックが機能する言語の良い例です。通常の文字列区切り文字は"…"また'…'はですが、あまり使用されないq(…)ネストで、この種のクインを記述できます。

$_=q($_=q(0);s/\d/$_/;print);s/\d/$_/;print

これはコードとデータの組み合わせです。s///正規表現の文字列置換操作です。0マーカーとして使用し、正規表現内で\d(「任意の数字」)として一致します。マーカーを複数回使用することを回避します(別の最適化として0、Perl s///は最初の出現をデフォルト)。q(…)データ文字列に区切り文字をそのまま含めることができるため、ここでは明示的なエスケープ手順は必要ありません。


8

コード+データカイン

クインの最も一般的な構造は、この擬似コードのようなものです。

data = " プログラム全体のエスケープバージョン、
        この文字列はマーカー "
プログラム= data.replace(
  マーカーと評価されたが、それを言及していないことを表現、
  escaped(data))
印刷プログラム。

この構造は、ほとんどの言語で(かなり素朴な)クインを書くために使用できます。ただし、プログラム全体を2回記述する必要があるため、ほとんどのスコアリングシステムではスコアがかなり低くなる傾向があります。ただし、ほとんどのクイン構造は、この構造の最適化と見なすことができます。

これにはいくつかの微妙な点があります。一部の言語では、この操作を実行する最も難しい部分は、エスケープコードを記述することです。多くの言語では、名前を言及せずにマーカーを作成することは困難です。また、一部の難解な言語では、独自の文字列リテラルを作成する必要があります。ただし、3つの操作はすべて、あまり問題を引き起こさない傾向があります。

たとえばrepr、を使用し、マーカーとして2文字のシーケンスx"文字列(として表される"x\""、つまりx"文字列自体の文字列表現のシーケンスを使用しない)を使用して、文字列をエスケープするPythonクインを記述できます。

d='d=x"\nprint(str.replace(d,"x\\"",repr(d)))'
print(str.replace(d,"x\"",repr(d)))

2
エソランでは、マーカーの位置に文字列を挿入するとコストがかかることが多く、文字列自体が最初または最後のものになるようにコードを構造化する価値があるかもしれないことに注意してください(おそらく別の答えで)ハードコーディングできる1つまたは2つの文字で終わります)。
マーティンエンダー

@MartinEnder:言及する価値があることに同意しますが、おそらく別の答えです(この答えのコメントや編集ではありません)。ほとんどのクインのヒントはこの一般的な構造の修正です。そのため、多くの人がクインを書くための出発点を知らないので、最初にそれをヒントとして公開したかったのです。

マーカーの代わりに2つの文字列を使用することもできます。これはGlassで行いました
Ørjanヨハンセン

4

ソースコードをラップするエクスプロイト

かなりの数の言語(主に2D言語)では、ソースコードがラップアラウンドできます。特定の状況(たとえば、プログラムが1行の場合はBefunge-98)では、プログラムの終わりを過ぎると、プログラムの最初に戻ります。この種の非線形動作は、文字列リテラルの内側と外側にあるコードを同時に記述できることを意味します。一致しない"(または文字列の区切り文字が何であっても)プログラムの残りの部分全体("それ自体を除く)を含む文字列を効果的に提供します。

このトリックを使用する際の問題の1つ"は、プログラムの開始からではなく、の観点から文字列を取得することです(あなたが望むように)。そのため、プログラム"が最初または最後に表示されるようにプログラムを再配置するのがおそらく最も簡単です。これは、多くの場合、プログラムを複数の部分に分割し、言語にある興味深い/異常なフロー制御コマンドを使用することを意味します(プログラムを文字列リテラルでラップできるほとんどの言語には、これらの適切な選択があります)。

良い例は、@ Justin's Befunge-98 quineです:

<@,+1!',k9"

"プログラムの最後の不一致は、プログラム全体を文字列リテラルでラップするため、(<開始のため右から左に実行する)必要なのは、プログラムを出力(9k)し、次に二重引用符('!1+,)を出力することです終了(@)。これにより、プログラムの2つのコピー(コードとして1つ、データとして1つ)を節約できます。2つのコピーは同じコードであり、異なる方法で解釈されるだけです。


4

クインの構造を覚えておいてください

私は、クインを2 つではなく3つの部分と考えるのが好きです。

  • パート1:パート2および3のデータ表現を生成します。
  • パート2:データを使用して、パート1をアルゴリズムで印刷します。
  • パート3:表現をデコードして、パート2および3を印刷します。

これにより、クインについて考えるのが簡単になります。以下は、各行がパーツに対応するPythonクインです。

s = "print(\"s = \" + repr(s))\nprint(s)"
print("s = " + repr(s))
print(s)

eval重複を削除するためにを使用することもありますが、一般的に、これは単純なクインの作成に役立つことがわかっています。

2つの異なるUnderload quinesを見てみましょう。これが最初です:

(:aSS):aSS

最初の部分は(:aSS)、データ表現を生成するです。2つ目は:aS、を印刷し(:aSS)ます。3番目の部分はS、印刷され:aSSます。

2番目のクインは次のとおりです。

(:aS(:^)S):^

最初は、これは収まらないようです。ただし、クインを展開すると、次のようになります。

(:aS(:^)S):aS(:^)S

さて(:aS(:^)S)、パート1で:aSパート2であり、(:^)S第3部です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.