Perl、1116 1124バイト、n = 3、score = 1124 ^(2/3)または約108.1
更新:これは、ブルートフォースによってn = 3で機能することを確認しました(これには数日かかりました)。この複雑なプログラムでは、放射線耐性を手動で確認するのは困難です(以前のバージョンで1つのミスを犯したため、バイトカウントが増加しました)。更新を終了
見えない場所にstderrをリダイレクトすることをお勧めします。このプログラムは、文字を削除していない場合でも、疑わしい構文に関する大量の警告を生成します。
プログラムを短縮できる可能性があります。これに取り組むことはかなり苦痛であり、可能な最適化を逃しやすくします。私は主に削除可能なキャラクターの数をできるだけ多くすることを目指していました(それはプログラムの本当に難しい部分だからです)、そしてコードとゴルフのタイブレークを狙うのに良いものとして扱いましたが、私は入れません最適化へのばかげた努力(放射能抵抗を偶然に破壊することは非常に簡単であることに基づいて)。
プログラム
注:_
の4つの出現のそれぞれの直前に、リテラルのControl- 文字(ASCII 31)があり-+
ます。StackOverflowに正しくコピーして貼り付けるとは思わないので、プログラムを実行する前に再度追加する必要があります。
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
説明
このプログラムは、まったく同じように、4つの同じ小さなプログラムを連結して構成されています。基本的な考え方は、プログラムの各コピーが、実行するには損傷がひどすぎるかどうかを検証することです。実行されていた場合、警告を発すること以外は何もせず、次のコピーを実行します。削除されていない場合(つまり、削除されていない、または削除された文字がプログラムの動作に影響を及ぼさない文字である場合)、適切な処理を行います(完全なプログラムのソースコードを出力します。これは適切なクインです。各部分にソースコード全体のエンコードが含まれる)、終了します(他の破損していないコピーがソースコードを再度印刷するのを防ぎ、テキストを大量に印刷してクインを台無しにします)。
各パーツは、機能的に独立した2つのパーツで構成されています。外部ラッパーと内部コード。そのため、それらを個別に検討できます。
外部ラッパー
外側のラッパーは、基本的にeval<+eval<+eval< ... >####>####...>###
(プラスの目的がかなり明確であるセミコロンと改行の束です;セミコロンの一部、またはそれらの前の改行が削除されたかどうかにかかわらず、プログラムの部分が分離されたままになるようにするためです。 )。これはかなり単純に見えるかもしれませんが、いくつかの点で微妙であり、この挑戦のためにPerlを選んだ理由です。
最初に、破損していないプログラムのコピーでラッパーがどのように機能するかを見てみましょう。eval
1つの引数を取る組み込み関数として解析します。議論が予想されるため、+
ここに単項式+
があります(これは、今ではPerlゴルファーによく知られています。驚くほど頻繁に役に立つでしょう)。私たちはまだ引数を期待しています(単項演算子を見たばかりです)ので、<
次に来るのは<>
演算子の開始として解釈されます(プレフィックスまたはポストフィックス引数を取らないため、オペランドの位置で使用できます)。
<>
かなり奇妙な演算子です。通常の目的はファイルハンドルを読み取ることであり、ファイルハンドル名を山括弧内に配置します。あるいは、式がファイルハンドル名として有効でない場合、グロビングを行います(基本的に、UNIXシェルがユーザーが入力したテキストをコマンドライン引数のシーケンスに変換するために使用するプロセスと同じです;実際に使用されるPerlのはるかに古いバージョンこのためのシェルですが、最近ではPerlは内部的にグロビングを処理します)。したがって、使用目的はの行に沿っており、<*.c>
通常はのようなリストを返し("foo.c", "bar.c")
ます。スカラーコンテキスト(引数への引数など)eval
)、最初に実行されたときに最初に検出された最初のエントリ(最初の引数に相当)を返すだけで、将来発生しない仮想実行で他のエントリを返します。
現在、シェルはしばしばコマンドライン引数を処理します。-r
引数なしのようなものを指定した場合、その名前のファイルがあるかどうかに関係なく、そのままそのままプログラムに渡されます。Perlは同じように動作します。そのため、<
との間にシェルまたはPerlに特別な文字がないことを確認する限り、これ>
を文字列リテラルの本当に厄介な形式のように効果的に使用できます。さらに良いことに、Perlのクォートのような演算子用のパーサーは、このようなコンテキストが意味をなさない場合でもブラケットを一致させる傾向があるため、<>
安全にネストすることができます(このプログラムを可能にするために必要な発見です)これらのすべてのネストの主な欠点は<>
、コンテンツのエスケープです<>
ほとんど不可能です。それぞれに2層のエスケープ解除があるように見えるため<>
、3つすべての内側から何かをエスケープするには、63個のバックスラッシュを前に付ける必要があります。コードサイズはこの問題の副次的な考慮事項にすぎませんが、スコアにこの種のペナルティを払う価値はほとんどないので、問題のある文字を使用せずにプログラムの残りを書くことにしました。
では、ラッパーの一部が削除されるとどうなりますか?
- 単語を削除すると、意味のない文字列で
eval
あるbarewordに変わります。Perlはこれらを好みませんが、引用符で囲まれているように扱います。したがってeal<+eval<+...
、"eal" < +eval<+...
。これはプログラムの動作に影響を及ぼしません。なぜなら、基本的には、ネストされたevals(とにかく使用しない)から結果を取得し、整数にキャストし、その上で無意味な比較を行うからです。(この種のことは、通常の状況では明らかに有用ではないため、多くの警告スパムを引き起こします。削除を吸収するためにそれを使用しているだけです。)これにより、必要な閉じ山かっこの数が変わります(開きかっこ代わりに比較演算子として解釈されるようになりました)が、最後にある一連のコメントにより、文字列が何回ネストされても安全に終了することが保証されます。(#
ここで厳密に必要とされるよりも多くの兆候があります。プログラムをより圧縮可能にし、クインを保存するためにより少ないデータを使用できるようにするために書きました。)
- 場合は
<
削除される、コードは今のように解析しますeval(eval<...>)
。eval
評価対象のプログラムは、プログラムとして実際の効果をもたらすものを何も返さないので、2番目の外部は効果がありません(通常返される場合、通常はヌル文字列またはベアワードです;より一般的には、例外eval
。null文字列を返すかexit
、まったく返さないようにするために使用します)。
+
が削除された場合、隣接するコードが損なわれていない場合、これはすぐには影響しません。単項+
はプログラムに影響しません。(元+
のsが存在する理由は、損傷の修復を支援するためです。関係演算子ではなく<
単項として解釈される状況の数が増えます。<>
つまり、無効なプログラムを作成するには、さらに削除する必要があります。)
ラッパーは十分な削除で破損する可能性がありますが、解析されない何かを生成するには、一連の削除を行う必要があります。4つの削除により、これを行うことができます。
eal<evl<eval+<...
また、Perlでは、関係演算子<
は非連想演算子であるため、構文エラーが発生します(同じエラーが発生します1<2<3
)。そのため、記述されているプログラムの上限はn = 3です。単項変数を追加する+
ことは、それを増やす有望な方法のように思えますが、ラッパーの内部も壊れる可能性がますます高まるため、プログラムの新しいバージョンが機能することを確認するのは非常に困難です。
ラッパーが非常に貴重である理由はeval
、Perlで(たとえば)構文エラーをコンパイルしようとすると発生する例外などの例外をキャッチするためです。これeval
は文字列リテラルであるため、文字列のコンパイルは実行時に行われ、リテラルがコンパイルに失敗すると、結果の例外がキャッチされます。これによりeval
、null文字列が返され、エラーインジケーター$@
が設定されますが、いずれのチェックも行われません(プログラムのいくつかの変更されたバージョンで、返されたnull文字列を時々実行する場合を除く)。重要なのは、これは、内部のコードに何かが発生した場合に、ラッパーは構文エラーを引き起こし、その後、ラッパーはコードに代わりに何もさせません(そして、プログラムはそれ自体の破損していないコピーを見つけようとして実行を続けます)。したがって、内部コードは、ラッパーと同じくらい耐放射線性である必要はありません。私たちが気にするのは、破損している場合、プログラムの破損していないバージョンと同じように動作するか、クラッシュするか(eval
例外をキャッチして続行できる)、何も印刷せずに正常に終了することです。
ラッパーの内側
ラッパー内のコードは、基本的には次のようになります(再び、Control _
があります-Stack Exchange はの直前に表示されません-+
)。
eval+(q(...)=~y=A-Z=-+;-AZz-~=r)
このコードは、グロブ安全な文字で書かれた、その目的は、文字列リテラル(私たちが使用することはできませんが翻字と評価を経て、実際のプログラムを記述することを可能にする句読点の新しいアルファベットを追加することです'
か、"
私たちの引用符としてただし、q(
… )
はPerlで文字列を形成する有効な方法です)。(印刷できない文字の理由は、プログラムでリテラルのスペース文字を使用せずに何かをスペース文字に音訳する必要があるためです。したがって、ASCII 31で始まる範囲を形成し、スペースを範囲の2番目の要素としてキャッチします。)明らかに、音訳を介していくつかのキャラクターを作成している場合、文字を犠牲にして、、大文字はあまり有用ではなく、句読点にアクセスするよりも、大文字にアクセスする方がはるかに簡単に書くことができます。
これは、グロブの結果として使用可能になる句読点のアルファベットです(上の行はエンコードを示し、下の行はエンコードする文字を示します)。
BCDEFGHIJKLMNOPQRSTUVWXYZ
! "#$%& '()* +; <=>?@ AZz {|}〜
最も注目すべきは、globセーフではないが、スペース文字と一緒にPerlプログラムを書くのに役立つ句読点がたくさんあることです。また、2つの大文字、リテラルA
and Z
(これは自分自身ではなく、T
およびU
にエンコードします。これA
は、上限および下限のエンドポイントとして必要だったためです)。これにより、新しいエンコードされた文字セットを使用して音訳命令自体を書くことができます(大文字はそれほど便利ではありませんが、大文字への変更を指定するには便利です)。利用できない最も注目すべき文字は、、およびですが[
、どれも必要ありません(出力に改行が必要なときは、暗黙的な改行を使用して生成しました\
]
say
書く必要はあり\n
ません。chr 10
うまくいきますが、より冗長です)。
いつものように、ラッパーの内部が文字列リテラルの外側で破損した場合、どうなるかを心配する必要があります。破損eval
すると、何も実行できなくなります。大丈夫です。引用符が破損すると、文字列の内側が有効なPerlではないため、ラッパーがそれをキャッチします(文字列の多数の減算は、有効なPerlを作成できたとしても、何もしないことを意味します。許容できる結果です)。音訳の損傷、それは構文エラーでない場合は、一般的に引き起こして、評価されている文字列をマングルされます、それは構文エラーになるために。これが壊れるケースがないか100%確信しているわけではありませんが、現時点ではそれを強引に強制しているので、もしあれば修正するのは簡単です。
エンコードされたプログラム
私が使用するエンコーディング、および追加の空白を逆に、文字列リテラル内で見ると、それを読みやすくするために、我々は、これは(再び、前の制御、アンダースコアを想像し得る-+
としてエンコードされ、A
):
$o=q<
length$o ==181 || zzzz((()));
do {
chop ($t = "eval+<"x4);
$r = '=-+;-AZz-~=';
$s = '$o=q<' . $o . '>;eval$o';
eval '$s=~y' . $r . 'A-Z=';
say "$t(q($s)=~y=A-Z${r}r)" . "####>"x6;
say ";" for 1..4
} for 1..4;
exit>;
eval $o
クインに慣れている人は、この一般的な構造を認識するでしょう。最も重要な部分は最初にあり、$ oが破損していないことを確認します。文字が削除された場合は、その長さが一致しません181
、我々は実行するので、zzzz((()))
それが原因比類のないブラケットに構文エラーでない場合のいずれもいるので、あなたが任意の3つの文字を削除しても、ランタイムエラーになりますこれ、zzzz
、zzz
、zz
、そしてz
関数であり、削除(((
して曖昧な構文エラーを引き起こす以外の関数として解析するのを防ぐ方法はありません。小切手自体も損傷を受けません。は||
破損する可能性があります|
が、それによりzzzz((()))
呼び出しが無条件に実行されます。あなたのいずれかを比較しているので、不一致が発生します変数や定数を損傷し0
、180
、179
、178
の桁のいくつかのサブセットに平等のために181
、1つ=
を削除すると解析エラーが発生し、2つ=
を削除するとLHSが整数0またはNULL文字列に評価され、どちらも偽になります。
更新:以前のバージョンのプログラムではこのチェックが少し間違っていたため、問題を修正するために編集する必要がありました。デコード後の以前のバージョンは次のようになりました。
length$o==179||zzzz((()))
これを取得するために最初の3つの句読点を削除することができました。
lengtho179||zzz((()))
lengtho179
、裸語であることは真実であり、したがってチェックを破ります。B
空白文字をエンコードする余分な2 文字を追加することでこれを修正しました。つまり、最新バージョンのクインはこれを行います。
length$o ==181||zzzz((()))
構文エラーを生成せずに=
記号と$
記号の両方を非表示にすることは不可能になりました。(私はの長さがあるため二つの空間ではなく、1を追加する必要がありました180
リテラル置く0
整数比較成功裸の単語、とゼロ。この文脈で悪用される可能性がソースの中に文字を)終了更新
長さのチェックに合格すると、少なくとも文字が削除されているという点で、コピーが破損していないことがわかります。そのため、そこからすべてが単純なクインです(破損したデコードテーブルによる句読点の置換は、このチェックでは検出されません) 、しかし、デコードテーブルのみから3つの削除がクインを壊さないことをブルートフォースで既に確認しました;おそらくそれらのほとんどが構文エラーを引き起こすでしょう)。$o
すでに変数を持っているので、外側のラッパーをハードコードするだけです(ある程度の圧縮を行います。質問のcode-golfの部分は完全に省略しませんでした)。1つの秘isは、エンコードテーブルの大部分を$r
; 内部ラッパーのエンコードテーブルセクションを生成するために文字どおりに印刷するかeval
、デコードプロセスを逆に実行するためにその周りにいくつかのコードを連結します(エンコードされたバージョンの$ oがわかるようにします) 、この時点でデコードされたバージョンのみが利用可能です)。
最後に、完全なコピーであり、元のプログラム全体を出力できる場合exit
、他のコピーもプログラムを印刷しようとするのを防ぐために呼び出します。
検証スクリプト
あまりきれいではありませんが、誰かが尋ねたのでそれを投稿します。私はさまざまな設定でこれを数回実行しました(通常は変更$min
し$max
、関心のあるさまざまな領域をチェックします)。完全に自動化されたプロセスではありませんでした。他の場所でのCPU負荷が重いため、実行を停止する傾向があります。これが起こったとき、私$min
は最初の値に変更し、$x
完全にはチェックされず、スクリプトの実行を続けました(したがって、範囲内のすべてのプログラムが最終的にチェックされるようになりました)。プログラムの最初のコピーからの削除だけをチェックしました。これは、他のコピーからの削除ではそれ以上のことができないことは明らかだからです。
use 5.010;
use IPC::Run qw/run/;
undef $/;
my $program = <>;
my $min = 1;
my $max = (length $program) / 4 - 3;
for my $x ($min .. $max) {
for my $y ($x .. $max) {
for my $z ($y .. $max) {
print "$x, $y, $z\n";
my $p = $program;
substr $p, $x, 1, "";
substr $p, $y, 1, "";
substr $p, $z, 1, "";
alarm 4;
run [$^X, '-M5.010'], '<', \$p, '>', \my $out, '2>', \my $err;
if ($out ne $program) {
print "Failed deleting at $x, $y, $z\n";
print "Output: {{{\n$out}}}\n";
exit;
}
}
}
}
say "All OK!";
Subleq
ます。この種の挑戦には理想的だと思います!