Perl, 1116 1124 bayt, n = 3, skor = 1124 ^ (2/3) veya yaklaşık 108.1
Güncelleme : Ben şimdi bunun n = 3 ile çalıştığını doğruladım (birkaç gün sürdü); Bu karmaşık bir programla, radyasyon direncini elle kontrol etmek zor (ve önceki sürümde bir hata yaptım, bu yüzden bayt sayısı arttı). Güncellemeyi sonlandır
Stderr'i görmeyeceğiniz bir yere yönlendirmenizi öneririm; Bu program bile şüpheli sözdizimi hakkında uyarılar bir ton üretir değildir ondan karakterleri silme.
Programın kısaltılması mümkündür. Bunun üzerinde çalışmak oldukça acı verici ve olası mikro optimizasyonları kaçırmayı kolaylaştırıyor. Çoğunlukla silinebilir karakterlerin sayısını olabildiğince yüksek tutmayı hedefliyordum (çünkü bu programın gerçekten zorlu bir parçası) ve kod-golf tiebreak'ini hedeflemem güzel bir şey olarak kabul etmedim ama optimizasyon için gülünç bir çaba (radyasyon direncini kazayla kırmanın çok kolay olması temelinde).
Program
Not: _
Her dört olaydan hemen önce hazır bir Kontrol karakteri (ASCII 31) vardır -+
. StackOverflow üzerine kopyalanıp yapıştırıldığını sanmıyorum, bu nedenle programı çalıştırmadan önce yeniden eklemeniz gerekecek.
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)####>####>####>####>####>####>
;
;
;
;
Açıklama
Bu program, oldukça açık bir şekilde bir araya getirilmiş dört aynı küçük programdan oluşuyor. Temel fikir, programın her kopyasının çalıştırılmayacak kadar kötü bir şekilde zarar görüp görmediğini doğrulayacağı; eğer öyleyse, hiçbir şey yapmaz (muhtemel uyarılar dışında) ve bir sonraki kopyanın çalışmasına izin verir; yapılmadıysa (yani silinme olmadıysa veya silinen karakter, programın işleyişinde fark yaratmayan bir karakterse), yapmayı istediği şeyi yapacaktır (tam programın kaynak kodunu yazdırarak; kaynak kodunun her birinin kodunu içeren her bir parça ile) ve sonra (diğer hasarsız kopyaların kaynak kodu tekrar yazdırmasını ve böylece çok fazla metin yazdırarak kesimi bozmasını önleyerek) çıkın .
Her bir bölüm, sırayla, etkin bir şekilde işlevsel olarak bağımsız olan iki bölümden oluşur; bir dış sarıcı ve bazı dahili kodlar. Bu nedenle onları ayrı ayrı değerlendirebiliriz.
Dış sarıcı
Dış ambalaj, temel olarak, eval<+eval<+eval< ... >####>####...>###
(artı amacı oldukça belirgin olması gereken bir sürü noktalı virgül ve yeni satır; bir noktalı virgül veya onlardan bazılarının silinmemesine bakılmaksızın, programın bölümlerinin ayrı kalmasını sağlamaktır. ). Bu oldukça basit görünebilir, ancak birkaç yönden ince ve bu meydan okuma için Perl'i seçmemin nedeni.
İlk olarak, paketleyicinin programın hasarsız bir kopyasında nasıl çalıştığına bakalım. eval
Bir argüman alan yerleşik bir işlev olarak ayrıştırır. Bir argüman beklendiğinden, +
burada bir birlik vardır +
(şu anda Perl golfçülerine çok aşina olacak; şaşırtıcı şekilde sıkça faydalı olacaklardır). Hala bir argüman bekliyoruz (daha önce bir unary operatörü gördük), bu nedenle bir <
sonraki adım operatörün başlangıcı olarak yorumlanır <>
(önek veya postfix argümanları almaz ve bu nedenle operand konumunda kullanılabilir).
<>
oldukça garip bir operatör. Onun her zamanki amaç filehandles okumaktır ve dt adı yerleştirilmesi iç açılı ayraçlar. Alternatif olarak, ifade bir dosya askısı adı olarak geçerli değilse, genelleme (temel olarak, UNIX'in kabuklarının, kullanıcı tarafından girilen metni komut satırı argümanlarının bir sırasına çevirmek için kullandığı işlem; bunun için kabuk, ancak bugünlerde Perl globbing'i içten ele alır). Bu nedenle amaçlanan kullanım, <*.c>
tipik olarak bir liste döndürecek olan çizgiler boyuncadır ("foo.c", "bar.c")
. Skaler bir bağlamda (argüman gibieval
), sadece ilk çalıştırıldığında bulduğu ilk girişi döndürür (ilk argümanın eşdeğeridir) ve asla gerçekleşmeyen varsayımsal gelecekteki çalışmalarda diğer girdileri döndürür.
Şimdi, kabuklar genellikle komut satırı argümanlarını ele alır; Eğer -r
tartışmasız bir şey verirseniz , bu isimde bir dosya olup olmadığına bakılmaksızın, sadece programa iletilir. Perl de aynı şekilde hareket eder, kabuk için veya karakter <
ile eşleşen arasında Perl'e özel karakter olmadığından emin olduğumuz sürece, bunu >
gerçekten garip bir dize değişkeni gibi kullanabiliriz. Daha da iyisi, Perl'in alıntı benzeri operatörler için ayrıştırıcısı, anlam ifade etmeyen bu gibi durumlarda bile parantezleri eşleştirme zorunluluğu vardır, bu yüzden <>
güvenli bir şekilde yuvalayabiliriz (bu programın mümkün olması için gerekli olanıdır ). Bu iç içe <>
geçmişlerin en büyük dezavantajı , içeriğinden kaçmaktır.<>
neredeyse imkansız; her biriyle birlikte açılmayan iki katman var gibi gözüküyor <>
, bu yüzden her üçünün de içindeki bir şeyden kaçmak için, önce 63 ters eğik çizgiyle gelmek gerekiyor. Kod büyüklüğünün bu problemde sadece ikincil bir değerlendirme olmasına rağmen, puanım için bu tür bir ceza ödemeye değmeyeceğine karar verdim, bu yüzden rahatsız edici karakterleri kullanmadan programın geri kalanını yazmaya karar verdim.
Peki, sargının parçaları silinirse ne olur?
- Kelimedeki silme,
eval
bir korkaklığa , anlamsız bir dizeye dönüşmesine neden olur . Perl bunlardan hoşlanmıyor, ama onlara tırnaklarla çevrilmiş gibi davranıyor; Böylece eal<+eval<+...
yorumlanır"eal" < +eval<+...
. Bunun programın çalışması üzerinde hiçbir etkisi yoktur, çünkü temelde sadece yoğun şekilde iç içe geçmiş (zaten kullanmadığımız) evrelerin sonucunu almak, bir tam sayıya dönüştürmek ve üzerinde bazı anlamsız karşılaştırmalar yapmaktır. (Bu tür bir şey, normal şartlar altında yapılması açıkça yararlı olmayan bir şey olduğundan çok fazla uyarı spamına neden olur; yalnızca silme işlemlerini absorbe etmek için kullanıyoruz.) Bu, ihtiyaç duyduğumuz kapanma açısı dirseklerinin sayısını değiştirir (çünkü açılış braketi şu anda bunun yerine bir karşılaştırma operatörü olarak yorumlanmaktadır), ancak sondaki yorum zinciri, kaç kez iç içe olursa olsun dizenin güvenli bir şekilde bitmesini sağlar. (Burada #
kesinlikle gerekenden çok daha fazla işaret var; programı daha sıkıştırılabilir kılmak için yaptığım gibi yazdım, quin'i depolamak için daha az veri kullanmama izin verdim.)
- Bir
<
silinirse, kod şimdi ayrıştırılır eval(eval<...>)
. İkincil, dışarının eval
hiçbir etkisi yoktur, çünkü değerlendirmekte olduğumuz programlar, program olarak herhangi bir gerçek etkisi olan hiçbir şey döndürmezler (eğer normal şekilde dönüyorlarsa, normalde boş bir dize veya bir korkak; eval
Bir boş dize döndürmeye neden olan istisna, ya da exit
hiç döndürmemek için kullanılır.
- Bir
+
silinirse, bitişik kodun sağlam olması durumunda bunun hemen bir etkisi olmaz; unary +
program üzerinde hiçbir etkisi yoktur. (Orijinal +
belgenin orda olması sebebi, hasarı onarmaya yardım etmektir; bunlar ilişkisel bir operatör olarak değil <
, bir birleşik olarak yorumlanan durumların sayısını arttırır <>
, yani geçersiz bir program üretmek için daha fazla silmeye ihtiyacınız vardır.)
Sarıcı , yeterli silme işlemiyle zarar görebilir, ancak ayrıştırmayan bir şey üretmek için bir dizi silme işlemi yapmanız gerekir. Dört silme ile bunu yapabilirsiniz:
eal<evl<eval+<...
Perl'de ilişkisel işleç ilişkisizdir <
ve bu nedenle bir sözdizimi hatası alırsınız (bununla aynı 1<2<3
). Bu haliyle, program için yazılan başlık n = 3'tür. Daha fazla unary eklenmesi +
, onu arttırmanın umut verici bir yolu gibi görünüyor, ancak programın yeni sürümünün çok zor olabileceğini doğrulamak için, sargının iç kısmının da kırılması olasılığını artırıyor.
Paketleyicinin bu kadar değerli olmasının nedeni, eval
Perl'de bir sözdizimi hatası derlemeye çalıştığınızda aldığınız istisnalar gibi (örneğin) istisnaları yakalamasıdır. Bu eval
bir dizgenin değişmezi olduğundan, dizginin derlemesi çalışma zamanında gerçekleşir ve değişmez derlenemezse ortaya çıkan istisna yakalanır. Bu, eval
boş bir dize döndürmeye ve hata göstergesini ayarlamasına neden olur $@
, ancak hiçbirini kontrol etmiyoruz (zaman zaman döndürülen boş dizeyi programın değiştirilmiş birkaç sürümünde yürütmek dışında). Önemli olan, bu, içindeki koda bir şey olması gerektiği anlamına gelir.Bir sözdizimi hatasına neden olan sarmalayıcı, daha sonra sarmalayıcı yalnızca kodun yerine hiçbir şey yapmamasına neden olur (ve program bozulmamış bir kopyasını bulma girişiminde çalışmaya devam eder). Bu nedenle, iç kodun sarıcı kadar radyasyona karşı dayanıklı olması gerekmez; Tek umursadığımız şey, eğer hasar görürse, programın zarar görmemiş sürümüyle aynı şekilde hareket edeceği veya başka bir şey yapmadan çökeceği ( eval
istisnayı yakalama ve devam etme izni veren ) veya normal olarak herhangi bir şey basmadan çıkacak olmasıdır .
Sarıcı içinde
Paketleyicinin içindeki kod temel olarak şöyle görünür (yine, _
Stack Exchange'in hemen önce göstermeyeceği bir Kontrol vardır -+
):
eval+(q(...)=~y=A-Z=-+;-AZz-~=r)
(Biz kullanamaz Bu kod glob güvenli karakterlerle tamamen yazılır, ve amacı transliterating ve bir dize değerlendirmek yoluyla, mümkün gerçek programı yazmak için yapmak noktalama işaretlerinden yeni bir alfabe eklemektir '
veya "
bizim alıntı olarak işaretler, ancak q(
… )
ayrıca Perl'de bir dize oluşturmak için geçerli bir yoldur). (Yazdırılamayan karakterin nedeni, programda değişmez bir boşluk karakteri olmadan bir şeyi boşluk karakterine çevirmemiz gerektiği, böylece ASCII 31'den başlayan bir aralık oluşturmamız ve alanı aralığın ikinci elemanı olarak yakalamamızdır.) Belli ki, eğer harf çevirisi yoluyla bazı karakterler üretiyorsak, karakterleri kopyalamak için karakterleri feda etmeliyiz ., ancak büyük harfler çok kullanışlı değildir ve noktalama işaretlerine erişime sahip olmayanlara yazmaksızın yazmak çok daha kolaydır.
Burada, glob sonucu elde edilebilen noktalama işaretlerinin alfabesi bulunur (üst satır kodlamayı gösterir, alt satır kodladığı karakter):
BCDEFGHIJKLMNOPQRSTUVWXYZ
! "# $% & '() * +; <? => @ Azz {|} ~
En önemlisi, dünya güvenliği açısından güvenli olmayan ancak Perl programlarını boşluk karakteriyle birlikte yazmakta yararlı olan bir sürü noktalama işaretimiz var. Ayrıca değişmez harfler büyük iki kaydedilir A
ve Z
(kodlama kendileri için, ama T
ve U
çünkü, A
bir üst sıra ve bir alt sınıfı için bir son nokta olarak gerekli); bu, yeni kodlanmış karakter setini kullanarak harf çevirisi talimatını yazmamıza izin verir (büyük harfler o kadar kullanışlı olmasa da, büyük harflerde değişiklik belirlemede kullanışlıdır). Elimizdeki olmadığını en önemli karakterlerdir [
, \
ve ]
, ama çıktıda bir yeni satır gerektiğinde hiçbiri (ihtiyaç vardır, ben örtülü yeni satır gelen kullanarak üretilensay
yazmaya ihtiyaç duymak yerine \n
; chr 10
Ayrıca işe yarayacaktı ama daha ayrıntılı.
Her zaman olduğu gibi, sargının iç kısmı telin dışına zarar verirse ne olacağı konusunda endişelenmemiz gerekir. Bozuk eval
herhangi bir şeyin çalışmasını önler; biz bununla iyiyiz. Alıntı işaretleri zarar görürse, dizginin içi geçerli Perl değildir ve dolayısıyla sarıcı yakalar (ve dizelerdeki sayısız çıkarma, geçerli Perl yapsanız bile hiçbir şey yapmaz. kabul edilebilir bir sonuçtur). Harf çevirisinde oluşacak hasar sözdizimi hatası değilse, değerlendirilmekte olan dizgiyi karıştıracak ve genellikle sözdizimi hatası oluşmasına neden olacaktır ; Bunun kırıldığı hiçbir durum olmadığından% 100 emin değilim, ancak şu anda emin olmak için kaba bir şekilde zorluyorum ve varsa düzeltmek için yeterince kolay olmalı.
Kodlanmış program
Dize değişmezine baktığımda, kullandığım kodlamayı tersine çevirip daha okunaklı hale getirmek için boşluk ekleyerek şunu elde ediyoruz (yine, -+
olarak kodlanandan önce kontrol alt çizgisini düşünün 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
Kuyruklara alışkın olan insanlar bu genel yapıyı tanıyacaktır. En önemli kısım, başlangıçta, $ o'nun hasarsız olduğunu doğruladığımız yer; karakterleri silinmiş olsa bile, uzunluğu maç olmayacak 181
Yürüttüğümüz bu yüzden, zzzz((()))
bunun nedeni eşsiz parantez bir sözdizimi hatası değilse, hangi nedeniyle hiçbiri arasında herhangi üç karakteri silseniz dahi bir çalışma zamanı hatası olacaktır zzzz
, zzz
, zz
, ve z
bir işlevdir ve silme (((
ve karmaşık bir sözdizimi hatasına neden olmaktan başka bir işlev olarak ayrıştırmasını engellemenin bir yolu yoktur . Kontrolün kendisi de hasara karşı bağışıklık kazanır; ||
için zarar görebilir |
ama bu neden olacaktır zzzz((()))
koşulsuz çalıştırmak için çağrı; Değişkenlere veya sabitlere zarar vermek uyumsuzluğa neden olacaktır, çünkü bunlardan birini karşılaştırıyorsanız 0
,180
, 179
, 178
Basamak bazı alt eşitlik için 181
; ve birinin kaldırılması =
ayrıştırma hatasına neden olur ve ikisi =
kaçınılmaz olarak LHS'nin her ikisi de falsey olan 0 tamsayısı veya boş bir dize olarak değerlendirmesine neden olur.
Güncelleme : Bu kontrol, programın önceki bir sürümünde biraz yanlıştı, bu yüzden sorunu çözmek için düzenlemek zorunda kaldım. Kodun çözülmesinden sonra önceki sürüm şöyle görünüyordu:
length$o==179||zzzz((()))
ve bunu almak için ilk üç noktalama işaretini silmek mümkündü:
lengtho179||zzz((()))
lengtho179
, bir korkak olmak, kaba ve bu nedenle çeki bozar. Bunu, ilave iki B
karakter ekleyerek (boşluk karakterlerini kodlayan) ekledim, yani sıranın en son sürümü şöyle:
length$o ==181||zzzz((()))
Artık hem =
işaretleri hem de işaretleri $
bir sözdizimi hatası üretmeden gizlemek imkansızdır . (Biri yerine iki boşluk eklemek zorunda kaldım, çünkü uzunluğu sıfır olan bir korkakla başarılı bir karakterle karşılaştırmak için bu bağlamda kötüye kullanılabilecek 180
bir 0
karakter niteliği taşıyordu.) Son güncelleme
Uzunluk kontrolü geçtikten sonra, kopyanın hasarsız olduğunu, en azından ondan karakter silmeleri açısından biliyoruz, bu yüzden hepsi oradan kolayca anlaşılıyor (bozuk bir kod çözme tablosundan dolayı noktalama işaretlerinin değişimleri bu çekle yakalanmayacaktı) , ancak, sadece kod çözme tablosundan üç silinme işleminin kaleyi kırmadığını, muhtemelen çoğunun sözdizimi hatalarına neden olduğunu) zorla zorlama yoluyla zaten doğruladım . $o
Zaten bir değişkene sahibiz , bu yüzden tek yapmamız gereken dış paketleyicileri kodlamak (küçük bir sıkıştırma derecesiyle; sorunun kod-golf kısmını tamamen atlamamıştım ). Bir hile, kodlama tablosunun büyük bölümünü içinde sakladığımızdır.$r
; biz ya içeride sargının kodlama tablosu bölümünü üretmek için tam anlamıyla yazdırabilir veya çevresinde bazı kodlar birleştirmek ve olabilir eval
bize ne olduğunu anlamaya izin (ters deşifreleme işlemini çalıştırmak için kodlanmış $ sürümünü o is bu noktada sadece kodu çözülmüş versiyona sahip ).
Son olarak, sağlam bir kopya olsaydık ve böylece tüm orijinal programı çıkartabilseydik exit
, diğer kopyaların da programı yazdırmaya çalışmasını önlemek için ararız .
Doğrulama betiği
Çok hoş değil, ama birileri sorduğu için gönderiyorum. Bunu çeşitli ayarlarla birkaç kez koştum (genellikle değişen $min
ve $max
çeşitli ilgi alanlarını kontrol etmek için); tam otomatik bir işlem değildi. Başka bir yerde ağır CPU yükü nedeniyle çalışmayı durdurma eğilimi vardır; bu olduğunda, sadece $min
ilk değeri $x
tamamen kontrol edilmedi ve senaryoyu çalıştırmaya devam ettim (bu yüzden aralıktaki tüm programların sonunda kontrol edilmesini sağladım). Yalnızca programın ilk kopyasındaki silme işlemlerini kontrol ettim, çünkü diğer kopyalardan yapılan silme işlemlerinin daha fazlasını yapamayacağı açıktır.
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
. Bu tür bir meydan okuma için ideal olacağını düşünüyorum!