Yazı yazmak için ipuçları


30

Bir programın kaynak koduna aynısı çıktı üreten bir programdır. Bu web sitesinde genel olarak sadece uygun quinleri önemsiyoruz (yazarken, mevcut tanım "çıktının bir kısmı programın farklı bir kısmı tarafından kodlanmış" dır).

Uygun sicim veya sicim benzeri özelliklere sahip programlar yazmak için ne öneriniz var? Her zamanki gibi, her ipucu farklı bir cevapta olmalıdır.

tips  quine 

Yanıtlar:


14

evalKod kopyalama ihtiyacını azaltmak için kullanın

Madenlerin çoğu kodun iki kopyasını gerektirir; Biri, biri veri olarak yürütülecek. Bu, kaynak kodun uzunluğunu iki katına çıkarabilir, korumayı zorlaştırabilir ve yarışması için aday olursanız skoru kötüleştirebilir .

İki kopyayı birleştirmek, bir bilgi parçasının iki amaç için kullanılması gerektiği anlamına gelir. Kodunuzu veri olarak ele almaya çalışmak çoğu zaman mümkün değildir ve genellikle olduğunda aldatma olarak kabul edilir. Bununla birlikte, verilerin kod olarak işlenmesi, genellikle denilen bir yerleşikin kullanılmasıyla birçok dilde yapılabilir eval. Bu nedenle, quine temel olarak, quine ana gövdesini bir değişkende saklamaktan (bir defadan daha fazla bahsedebilmeniz için) ve ardından bu değişkeni değerlendirmekten ibarettir.

İşte bunun nasıl çalıştığını gösteren bir örnek (örnek Python'da yazılmıştır, ancak benzer bir çok dilde benzer bir şey):

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

2
@QPaysTaxes: Kodumu burada zafer şartının izin verdiği ölçüde okunabilir ve sürdürülebilir hale getirmeyi amaçlıyorum. Ne yazık ki, bu hala tipik olarak ASCII hat gürültüsünden (veya Jelly kullanıyorsanız normal hat gürültüsünden) dile alışkın olmayan insanlardan ayırt edilemez.

14

String formatlamadan yararlanın

Bir kıskaç oluşturmanın en kolay yollarından biri bir dize tanımlamak, sonra da dize biçimlendirmeli dize kendi içine koymaktır.

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

Yani bu örnekte Python quine, ilk parçayı dizeden öncekine eşit olan bir dize olarak ilan ettik s=, daha sonra dizenin biçimlendirmeyle birlikte eklenmesine izin veriyoruz %rve son olarak dizgeden sonra yazdırmak ve biçimlendirmek için gelenleri koyacağız. . Sondaki yeni satır, sondaki yeni satırı printyazdırır.

Yani şablon gerçekten Python'da:

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

Mevcut sırayı daha fazla kodla genişletmek için:

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

9

Telli fonksiyonlar

Çeşitli dillerde, işlev nesneleri (veya eşdeğer yapılar) kaynak kodlarını örtük olarak saklar ve dizeye dönüştürüldüğünde döndürür. Bu olmadan kompakt quines sağlar string eval kullanmadan . Böyle bir dilin kayda değer bir örneği JavaScript'tir:

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

Bu kod, fçağrıldığında kendi kaynak kodunu ve ardından kendisine bir çağrı basan bir fonksiyon tanımlar ve çağırır . Programın kopyalanması gereken tek kısmı, işlev çağrısıdır f(). Elbette, işlev gövdesi, işlev çağrıldığında da yürütülecek olan isteğe bağlı bir kod yükünü içerebilir.


Aynı hilenin daha kompakt bir versiyonu golf dillerinde GolfScript ile çalışır :

{".~"}.~

ve CJam :

{"_~"}_~

Bu kuyrukların her biri önce JavaScript'teki bir işlev nesnesi gibi davranan adsız bir kod bloğunu (parantez içine alınmış) tanımlar: çalıştırılabilir ve dizilenirse kaynak kodunu döndürür. Kodun geri kalanı ( .~GolfScript veya _~CJam'da) daha sonra bloğu yürütür ve bir kopyasını yığında bırakır. Bloğun içindeki kod daha sonra kodu bloğun dışında tekrarlayan yığına iter. Tercüman çıkınca, yığın üzerinde kalan her şeyi otomatik olarak dizge eder ve yazdırır. JavaScript örneğinde olduğu gibi, bu kod blokları, çoğaltmak zorunda kalmadan rastgele bir ek kod yükü taşımak ve yürütmek için de yapılabilir.


9

Çıkmadan iç içe geçmiş dize sınırlayıcıları kullanın

Genellikle, bir yazı yazma hakkında en zor kısımlardan biri kaçan adımdır. Bu hemen hemen her quine için gereklidir; Sorun şu ki, bir şekilde veri saklıyorsunuz ve verileri çıktının çıkışında saklayan kodu çoğaltmanız gerekiyor. Bu kod, verinin çıkarılan bir formunu içerecektir, bu nedenle program çıkmamış bir form görecek ve yeniden kaçmanız gerekecek.

Çıkmayan adımı ele almanın en kolay yolu, verilerin kaçan ve çıkarılmayan biçimlerinin yalnızca dize sınırlayıcıların varlığında veya yokluğunda farklılık göstermesidir. Bu nedenle kaçma, dizenin etrafına yeni bir çift dizi sınırlayıcı eklemek için basit bir meseledir. Ne yazık ki, bu açıkça ancak string sınırlayıcıların kendileri kaçmadan veride ifade edilebiliyorsa işe yarayabilir.

Perl bu hilenin çalıştığı dilin güzel bir örneğidir. Her zamanki dize sınırlayıcıları, "…"ya '…'da daha az kullanılan q(…)yuvalar olmasına rağmen, bu tür bir sıranın yazılmasına izin verir:

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

Bu bir kod + veri dizisidir. s///bir regex string değiştirme işlemidir; Kullandığımız 0işaretleyici olarak ve aynı regex içinde eşleşmesi \d( "Herhangi bir rakam"), daha başka optimizasyonu gibi, biz aslında sadece kullanılmış olabilir, ancak (bir kez daha işaretleyici kullanarak önlemek için 0Perl en çünkü yine s///sadece ilk geçtiği yerini alır varsayılan). q(…)Sınırlayıcılar tam anlamıyla veri dizisine eklenebildiğinden , burada açık bir kaçma adımı gerekmediğine dikkat edin.


8

Kod + veri sorguları

Bir quine için en genel yapı, bu sözde kod gibi görünüyor:

veri = " tüm programın çıkışlı bir versiyonu,
        bu dize ile bir işaretleyici yerine "
program = data.replace (
  işaretçiyi değerlendiren ancak ondan bahsetmeyen bir ifade ,
  kaçan (veri))
yazdırma programı;

Bu yapı, çoğu dilde (oldukça saf) bir yazı yazmak için kullanılabilir. Ancak, çoğu puanlama sisteminde oldukça kötü puan alma eğilimindedir, çünkü programın tamamını iki kez yazmanız gerekir. Bununla birlikte, çoğu yüzgeç yapıları, bunun optimizasyonları olarak düşünülebilir.

Bunun bazı incelikleri var. Bazı dillerde, bu işlemi gerçekleştirmenin en zor kısmı, çıkış kodunu yazmaktır; birçok dilde, işaretleyicinin adını söylemeden üretmek zordur; ve bazı ezoterik dillerde, kendi dizinizdeki hazır yazı türünü icat etmeniz gerekecek. Her üç operasyon da çok fazla sorun yaratmaya meyilli değil.

Örneğin, bir dizgeden kaçan reprve 2 karakter dizili x"dizgiyi (dizgenin dizgesindeki dizilim göstergesindeki diziyi "x\""kullanmayacak şekilde x"gösterilebilir), işaretçi olarak kullanarak bir Python keki yazabiliriz :

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

2
Dize bir işaretleyicinin pozisyonuna yerleştirilmesinin, esolanjlarda genellikle pahalı olduğu ve dizenin kendisinin ilk veya son şey olduğu (belki de ondan ayrı olacak şekilde) kodun yapılandırılmaya değer olabileceği dikkate değer olabilir. Sonunda bir veya iki karakter yazabilirsin ki nereye koyacağını bilmek için).
Martin Ender

@MartinEnder: Söylemeye değer olduğu konusunda hemfikirim, ancak muhtemelen başka bir cevap (bu cevaptaki yorum veya düzenleme yerine). Çoğu genel ipucu, bu genel yapı üzerinde yapılan değişikliklerdir, bu yüzden birçok insan bir yazı yazma için nereden başlayacağına dair hiçbir fikri olmadığı için, kendi başına bir ipucu olarak ortaya çıkarmak istedim.

Bir işaretleyiciye bir alternatif iki tel kullanmaktır, bunu Glass için yaptım .
Ørjan Johansen

4

Kaydırma kaynak kodunu kullanma

Oldukça az sayıda dilde (çoğunlukla 2B dilde) kaynak kodu çevrilebilir; Belli koşullar altında (örneğin, Befunge-98'de, eğer programınız bir liner ise) programın sonundan geçerek sizi programın başlangıcına geri götürecektir. Bu tür doğrusal olmayan davranışlar, aynı anda bir dizgenin içine ve dışına kod yazabileceğiniz anlamına gelir; Eşsiz "(ya da dize sınırlayıcı her ne ise) etkili bir şekilde size programın geri kalanının tamamını ( "kendisi hariç ) içeren bir dize verecektir .

Bu numarayı kullanmanın bir sorunu, diziyi "programın başlangıcından (istediğiniz gibi) bakış açısından görüldüğü gibi alabilmenizdir . Bu nedenle, programı yeniden düzenlemek, muhtemelen "başında veya sonunda görünecek şekilde en kolay olanıdır . Bu genellikle, programınızın birden fazla parçaya bölünmesi ve dilinizin sahip olduğu ilginç / olağandışı akış kontrol komutlarından faydalanılması anlamına gelir (programın etrafına dizginin tam anlamıyla yazılmasını sağlayan çoğu dilde bunlardan iyi bir seçim yapabilirsiniz).

İyi bir örnek @ Justin'in Befunge-98 quine'i :

<@,+1!',k9"

Programın "sonunda eşleştirilmemiş olan tüm programı bir dizgede değişmez, yani ( <başlangıçta sağdan sola doğru koşar ) tek yapmamız gereken program çıktısını almaktır ( 9k), sonra çift alıntı ( '!1+,) çıktısını almak ve exit ( @). Bu, programın iki kopyasına ihtiyaç duyulmasını önler (biri kod, biri veri); iki kopya aynı kod parçasını oluşturuyor, sadece farklı şekillerde yorumlanıyor.


4

Bir quine yapısını hatırla

Kuyrukları 2 yerine üç parça olarak düşünmeyi seviyorum :

  • Bölüm 1: Bölüm 2 ve 3'ün bir veri gösterimini oluşturun.
  • Bölüm 2: Verileri kullanarak bölüm 1'i algoritmik olarak geri yazdırın.
  • Bölüm 3: 2. ve 3. bölümleri yazdırmak için gösterimin kodunu çözün.

Bu quines hakkında düşünmeyi kolaylaştırabilir. İşte her bir bölüme karşılık gelen bir Python quine:

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

Bazen, evalçoğaltmayı kaldırmak için bir veya benzerini kullanırsınız, ancak genel olarak bunun basit sorgu yazmada yardımcı olduğunu buldum.

İki farklı Underload sırasına bir göz atalım. Bu ilk:

(:aSS):aSS

İlk kısım (:aSS)veri temsilini oluşturan kısımdır . İkincisi :aS, hangi yazdırır (:aSS). Üçüncü kısım, Sbasılan şeydir :aSS.

İşte ikinci quine:

(:aS(:^)S):^

İlk başta, bu uygun görünmüyor. Ancak, sırayı genişletirseniz, şunları elde edersiniz:

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

Şimdi (:aS(:^)S)bölüm 1, :aSbölüm 2 ve (:^)Sbölüm 3.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.