Perl'de golf oynamak için ipuçları?


47

Perl'de golf oynamak için hangi genel ipuçlarınız var? Genel olarak golf problemlerini kodlamak için uygulanabilecek fikirleri arıyorum, en azından biraz Perl'e özgü (örneğin, "yorumları kaldır" bir cevap değil). Lütfen cevap başına bir ipucu gönderin.

Yanıtlar:


26

TMTOWTDI

Bilmeniz gereken en önemli Perl golf ipucu. Çok uzun bir dizi karaktere baktığınızda, görevinizi tamamlamak için mutlaka yapmanız gereken, farklı bir özellik kullanarak aynı etkiyi elde etmenin başka bir yolu olmadığını kendinize sorun. Genellikle var. İşte sadece bir avuç:

  • ~~bir skalar bağlamı zorlar ve 4 karakterden kısa scalar.

  • y///clengthuzunluğu elde edilenden daha kısa bir karakter $_.

  • İçindeki karakterleri yinelemeye $_mi ihtiyacınız var ? Değiştir split//ile /./gs. (Ya kullanımı /./gda yeni satır atlamak istiyorum.) Diğer değişkenlerle Bu eserler: yerine split//,$xile $x=~/./gs.

  • Her Perl yerleşik bir şey döndürür. printbaşarılı G / Ç'yi belirtmek için örneğin 1 değerini döndürür. $_Örneğin, gerçek bir değere sıfırlamanız gerekirse, $_=print$fooiki kuşu bir taşla öldürmenize izin verir.

  • Perl'deki hemen hemen her ifade, daha geniş bir bağlamda kullanılmasına izin vererek bir ifade olarak yazılabilir. Çoklu ifadeler, virgüllerle birlikte zincirlenmiş çoklu ifadeler haline gelebilir. Testler kısa devre yapan operatörlerle ?: && ||ve aynı zamanda andve oraynı şeyi yapan ancak önceliği diğer tüm operatörlerden daha düşük olan (atama dahil) yapılabilir. Döngüler üzerinden yapılabilecek mapya grep. Hatta anahtar kelimeler gibi next, lastve returnonlar dönmek yok olsa bile, bir ifade bağlamında kullanılabilir! Bu tür dönüşümlerin akılda tutulması size, kod bloklarını daha geniş bir bağlamda girebilecek ifadelerle değiştirme fırsatını verir.


$_=print""daha kısa $_=print$foo.
ASCIIThenANSI

5
Buradaki fikir, zaten yazdırmanız gerektiğidir $foo. Aksi takdirde, aynı etkiye $_=1göre çok daha kısa $_=print""ve daha kısadır .
breadbox

3
Üçüncüsü, içerideki kömürleri atlayarak mı demek istiyorsun $x? Aksi takdirde sadece /./gsve yapabilirdi /./g.
redbmk

25

Kötüye Perl'in özel değişkenleri!

  • Bir önceki yanıtta belirtildiği gibi $/ve $"varsayılan olarak başlatılır "\n"ve " "sırasıyla.

  • $,ve $\her ikisi de undefvarsayılan olarak ayarlanmıştır ve 3 karakter daha kısadır.

  • $\Bir değere ayarlamak , her birine eklenmesine neden olacaktır print. Örneğin: perl -ple '$\="=".hex.$/'kullanışlı bir hex-ondalık dönüştürücü.

  • Dosyaları komut satırından okuyamıyorsanız, -ikomut satırı anahtarını dize girmek için fazladan bir kanal olarak kullanabilirsiniz . Değeri depolanacaktır $^I.

  • $=kendisine verilenleri bir tamsayı olarak zorlar. Çalıştırmayı deneyin perl -ple '$_=$==$_'ve ona çeşitli inupts vererek. Benzer şekilde, $-değerini negatif olmayan bir tamsayı olmaya zorlar (yani, öncü bir çizgi sayısal olmayan bir karakter olarak değerlendirilir).

  • $.Bir while(<>)döngünün her yinelemesinde otomatik olarak gerçek (sıfır olmayan) bir değere sıfırlanan bir boolean bayrağı olarak kullanabilirsiniz .


20

-n ve eşsiz kıvrımlı parantez

Komut satırı anahtarının -nkomut dosyasını her satır için bir kez çalıştırmak için kullanılabileceği iyi bilinmektedir .

perl --help diyor:

  -n                assume "while (<>) { ... }" loop around program

Açıkça söylemediği şey, Perl’in sadece program etrafında bir döngü üstlenmemesi; o anlamıyla sarar while (<>) { ... }etrafında.

Bu şekilde, aşağıdaki komutlar birbirine eşdeğerdir:

 perl -e 'while(<>){code}morecode'
 perl -ne 'code;END{morecode}'
 perl -ne 'code}{morecode'

-p ve eşsiz kıvrımlı parantez

Yukarıdakilere benzer şekilde, -panahtar while (<>) { ... ; print }programın etrafına sarılır .

Eşsiz küme parantezleri kullanıldığında, tüm girdi satırları için perl -p 'code}{morecode'yürütüldükten sonra sadece bir kez basılır code, ardından gelir morecode.

Yana $_zaman tanımlanmamış morecode;printyürütüldüğünde, çıkış kayıt ayırıcı $\gerçek çıktı yazdırmak için kötüye kullanılabilir.

Örneğin

perl -pe '$\+=$_}{'

STDIN'den satır başına bir sayı okur ve toplamını yazdırır.


Bunu #!perl -nilk satırda başarabileceğini sanıyorum , değil mi?
ASCIIThenANSI

@ASCIIThenANSI: Evet, doğru. (Geç cevap için özür dilerim.)
Dennis,

1
Kredinin verildiği yerde kredi vermek, sanırım bu üç hilenin (eşsiz kıvrımlı parantez -pve $\​) kombinasyonunu ilk defa Primo'nun Perl cevaplarından birinde gördüm . Cevaplarını okumak, kendi başına iyi bir Perl ipucu.
Dennis,

1
Bir Fırlatma }for(...){destekler arasında genellikle de, örneğin oldukça kullanışlı codegolf.stackexchange.com/a/25632
primo

-N ile birlikte yararlı bulduğum bir şey || = default value atama işlecidir. Döngüden önce bir değer atayamamak.
deltaray

18

$_Skaler referansları ortadan kaldırmak için kullanın . Çoğu işlev tarafından varsayılan olarak kullanılan özel değişkendir ve sadece parametreleri dışarıda bırakmak, bu değişkeni referans almak için bir kısayoldur.

Değiştirerek $netmek $_, değiştirmek olabilir $n=<>;chop$n;print$niçin$_=<>;chop;print

Burada, printişlev $_varsayılan olarak içeriğini yazdırır ve üzerinde chopde çalışır $_.


Is $_=<>;gelmez, gerekli <>;konusunda çizgiyi okumak $_otomatik?
sundar

Hayır, sanmıyorum. Programları karşılaştırdım $_=<>;printve <>;print. İlki yazdığım şeyi bana geri veriyor, diğeri yazmıyor.
PhiNotPi

Oh, teşekkürler, bunun sadece böyle durumlarda olduğu ortaya çıktı print while(<>). Emin değilim, ne de özel bir durum ya da bazı tutarlı bir mantık bunun arkasında olup olmadığını <>'s parçası perlopne de whilebir parçası perlsynbu davranışı söz görünmektedir.
Sundar

1
@ sundar while(<>)perlsyn, G / Ç Operatörleri ile belgelenen özel bir durumdur: 'Eğer ve eğer giriş sembolü, bir "while" ifadesinin koşulunun içindeki tek şeyse (eğer (için;;;)) döngü), değer otomatik olarak $ _ genel değişkenine atanır, daha önce
olanları

17

Perl'in özel değişkenlerini, nerede olursanız olun kullanın, örneğin:

  • Yerine $"kullanın" "
  • Yerine $/kullanın"\n"

Lexer yardımı ile garantili bir karakter uzunluğunda bir tanımlayıcı olma avantajına sahiptirler. Bu, aşağıdaki gibi anahtar kelimeye yapıştırılmasını mümkün kılar:print$.for@_

Tüm özel değişkenlerin listesi burada mevcuttur: Özel Değişkenler


15

Kullanmayın qw. Bu daha iyi kullanılabilecek iki karakter kaybıdır. Örneğin, aşağıdakileri yazmayın.

@i=qw(unique value);

Bunun yerine korkaklar kullanın.

@i=(unique,value);

Veya korkak kullanamıyorsanız, globsözdizimini kullanın.

@i=<unique value>;

glob Sözdizimi ilginç etkiler için de kullanılabilir.

@i=<item{1,2,3}>;

12

Bileşik ifadeler yerine deyim değiştiricileri kullanın.

Bileşik ifadeler, argüman için parantez ve blok için parantez gerektirme eğilimindeyken, ifade değiştiricilerin ikisine de gerek yoktur.

Karşılaştırmak:

  • $a++,$b++while$n-- vs while($n--){$a++;$b++}
  • chop$,if$c vs if($c){chop$,}

Son örneğin $c&&chop$,, çoğu multi-ifade işlemi için birbiriyle bağlantılı olduğunu , ancak gerçekten parlamaya başladığını unutmayın. Temel olarak işleç önceliği kaybeden bir şey &&.


11

Yapma use strict. (bu konuda bana alıntı yapmayın, PCG.SE bağlamı biraz önemlidir) Ve daha da önemlisi, katı bir kural olarak kodlamayın. Olağan Şüpheliler:

  • Bunu myengelleyebilirseniz değişkenleri bildirmeyin. Gerçekten ihtiyaç duyan tek değişkenler, mysözcüksel olarak kapsamlaştırılmasını istediğinizlerdir. Bu, golf oynarken, kapsam korumasına ihtiyaç duymadığınız ve özyinelemeyi tamamen kontrol etme eğiliminde olduğunuz herhangi biri.
  • tek kelimeli dizelerden alıntı yapmayın: ( örnek ). Yine de aynı isimde bir işleve sahip olmadığınızdan emin olun.

5
print helloişe yaramayacak. Bu aslında print hello $_( $_filehandle yazdır hello) anlamına gelir .
Konrad Borowski

@GlitchMr teşekkürler! (ve şimdi kızgınım, çünkü benim açımdan hala geçerli, sadece değil print, ve şimdi güzel ve kısa bir örnek bulamıyorum)
JB

@JB burada iyi bir örnek: codegolf.stackexchange.com/a/8746/3348
ardnew

11

Bunların bazılarının resmi isimleri olduğundan eminim ve ben sadece onların farkında değilim.

  • Bir süre döngüsünüz varsa (veya bir döngü için bir süre döngüsü oluşturabilirsiniz) komuttan sonra "while" ifadesini tanımlayabilirsiniz: print $n++ while ($n < 10)
  • STDIN'den bir dizgeye her şeyi okumak gerekirse: $var = join('',<>)
  • CeilingSpy yerine / $ kullanılarak işaret \ n daha hızlı bazı durumlarda olduğu gibi: print ('X'*10) . "\n";daha uzun olduğuprint ('X'*10) . $/;
  • Perl'in sayişlevi şundan kısa print, ancak kodu -Eyerine çalıştırmak zorundasınız .-e
  • Gibi a..zveya hatta aralıkları kullanın aa..zz. Bir dize olarak gerekirse, kullanın join.
  • Artan dizeler: $z = 'z'; print ++$z;gösterecektiraa

Şu an tek düşünebildiğim bu. Daha sonra biraz daha ekleyebilirim.


1
Ne print ('X'*10) . $/;yapmalı? Benim için yazdırır 0ve yeni satır yok. Birincisi, parantez içinde printolandan daha sıkı bağlanan bir işlev tarzı çağrı argümanı olur .. Demek istediğin bir şey xyerine *mi?
aschepler

Postfix'de parantez gerekmez while, join'',<>;bunlar olmadan da çalışır.
choroba

10

Sözcük olmayan karakterleri değişken adları olarak kullanın

Kullanma $%yerine $ahemen yanında bir değişken adını yerleştirmek için izin verebilir if, forya da whileolduğu gibi inşa:

@r=(1,2,3,4,5);$%=4; print$_*$%for@r

Birçoğu kullanılabilir, ancak hangilerinin sihirli etkileri olduğu konusunda docs ve @ BreadBox'ın cevaplarını kontrol edin !


İfade değiştiricileri kullanamadığınızda haritayı kullanın

İfade değiştiricileri @ JB'in yanıtına göre kullanamazsanız , harita bir bayttan tasarruf sağlayabilir:

for(@c){} vs. map{}@c;

ve postfix fordöngülerinin içine koyabileceğiniz gibi iç içe yineleme yapmak istiyorsanız kullanışlıdır map.


Tüm sihirli Normal İfade değişkenlerini kullanın

Perl 'eşleşme öncesi metin' ve 'eşleşme sonrası metin' için sihirli değişkenlere sahiptir, bu nedenle potansiyel olarak daha az karakter içeren iki gruba ayrılmak mümkündür:

($x,$y)=split/,/,$_;
($x,$y)=/(.+),(.+)/;
/,/; # $x=$`, $y=$'
# Note: you will need to save the variables if you'll be using more regex matches!

Bu ayrıca bunun yerine geçebilir substr:

$s=substr$_,1;
/./;# $s=$'

$s=substr$_,4;
/.{4}/;# $s=$'

Maçın içeriğine ihtiyacınız olursa $&, örneğin:

# assume input like '10 PRINT "Hello, World!"'
($n,$c,$x)=split/ /,$_;
/ .+ /; # $n=$`, $c=$&, $x=$'

Subleri uzun isimlerle daha kısa isimle değiştir

printKodunuzda dört veya daha fazla kez söylerseniz (bu, aradığınız rutinin uzunluğuna göre açıkça değişir), daha kısa bir alt isimle değiştirin:

sub p{print@_}p;p;p;p

vs.

print;print;print;print

Koşullu artırıcıları / azaltıcıları değiştirin

Gibi bir kod varsa:

$i--if$i>0

kullanabilirsiniz:

$i-=$i>0

bunun yerine bazı baytları kaydetmek için


Tamsayıya dönüştür

Bir değişkeni atamıyorsanız ve breadbox'ın ipucunu kullanamıyorsanız, şu ifadeyi kullanabilirsiniz 0|:

rand 25 # random float eg. 19.3560355885212

int rand 25 # random int

0|rand 25 # random int

rand 25|0 # random int

~~rand 25 # random int

Ancak, bir dizi indeksine erişmek için bir tamsayı kullanmanıza gerek kalmayacağından dikkat çekiyor:

@letters = A..Z;
$letters[rand 26]; # random letter

9

redoforveya olmayan bir bloğa döngü davranışı ekler while. {redo}sonsuz bir döngüdür.


7

İşlev çağrılarını parantez içine almayın.

Perl, NAME LISTsözdizimini kullanarak bilinen (çekirdek veya önceden belirlenmiş) bir işlevi çağırmanıza olanak tanır . Bu &, parantezin yanı sıra (hala kullanıyorsanız) sigilleri bırakmanıza izin verir .

Örneğin: $v=join'',<>

Tam belgeler


5

Atama ifadesinin değerini kullanmayı deneyin:

# 14 characters
$n=pop;$o=$n&1

# 13 characters, saves 1
$o=($n=pop)&1

Bu çalışıyor çünkü $nPerl'de 2 karakter var. Sen değişebilir $niçin ()hiçbir ücret ödemeden ve parantez içine atama hareket ettirerek 1 noktalı virgül kaydedin.


5

İç içe üçlü mantık içinde birden çok farklı ifadeyi çalıştırabilirsiniz.

Büyük bir olduğunu varsayalım if- elsifdeyimi. Bu herhangi bir mantık ve herhangi bir sayıda ifade olabilir.

if( $_ < 1 ) {
    $a=1;
    $b=2;
    $c=3;
    say $a.$b.$c;
} elsif($_ < 2 ) {
    $a=3;
    $b=2;
    $c=1;
    say $a.$b.$c;
} elsif( $_ < 3) {
    $a=2;
    $b=2;
    $c=2;
    say $a.$b.$c;
}

(cmd1, cmd2, cmd3)Tüm komutları çalıştırmak için üçlü operatörün içinde kullanabilirsiniz .

$_ < 1 ?
    ($a=1,$b=2,$c=3,say$a.$b.$c):
$_ < 2 ?
    ($a=3,$b=2,$c=1,say$a.$b.$c):
$_ < 3 ?
    ($a=2,$b=2,$c=2,say$a.$b.$c):
0; #put the else here if we have one

İşte sahte bir örnek:

perl -nE'$_<1?($a=1,$b=2,$c=3,say$a.$b.$c):$_<2?($a=3,$b=2,$c=1,say$a.$b.$c):$_<3?($a=2,$b=2,$c=2,say$a.$b.$c):0;' <(echo 2)

4

Yerine select(undef,undef,undef,$timeout)kullanınTime::HiRes

( Https://stackoverflow.com/a/896928/4739548 adresinden alınmıştır )

Birçok zorluk, tamsayılardan daha hassas biçimde uyumanızı gerektirir. select()'ın zaman aşımı argümanı sadece bunu yapabilir.

select($u,$u,$u,0.1)

şunlardan çok daha verimli:

import Time::HiRes qw(sleep);sleep(0.1)

İlki yalnızca 20 bayttır, ikincisi ise 39'u alır. Bununla birlikte, birincisi kullanmamanızı $uve asla tanımlamanızı gerektirmez.

Eğer çok kullanacaksanız ithalatı karşılığını alır Time::HiRes, ancak sadece bir kez ihtiyacınız olursa, select($u,$u,$u,0.1)19 byte tasarruf sağlar, bu çoğu durumda kesinlikle bir gelişmedir.


1

Basılı ifadelerinizi kısaltın

Mücadelenin aksi belirtilmediği sürece, takip eden yeni satırlara ihtiyacınız yoktur.
'Mücadelemiz', '0'dan 9'a STDOUT'a rasgele bir sayı çıktı' diyor. Bu kodu alabiliriz (28 bayt):

$s=int(rand(10));print"$s\n"

Ve bunu kısaltın (25 bayt):

$s=int(rand(10));print $s

sadece değişkeni yazdırarak. Bu sonuncusu yalnızca özellikle bu zorluk için geçerlidir (19 byte):

print int(rand(10))

ancak bu yalnızca atama ve yazdırma arasındaki değişkene hiçbir şey yapmanız gerekmediğinde işe yarar.


Sonuncusu zaten burada listelenmiştir .
Sp3000

@ Sp3000 Teşekkürler, cevabımı güncelledim.
ASCIIThenANSI

1

String değişmezleri gibi globlar kullanın

Zaman zaman (çoğu zaman veya zorluklarıyla uğraşırken ) dize değişmezlerini yerleştirme kabiliyetinden büyük ölçüde faydalanırsınız. Normalde bunu birlikte yaparsın q(…). Bununla birlikte, dizgideki hangi karakterlere ihtiyacınız olduğuna bağlı olarak <…>, glob işlecini bir bayt ve kullanım olarak kaydedebilirsiniz . (Köşeli parantezlerin içindekilerin bir dosya askısı gibi görünemeyeceğini ve bir dosya listesi listesine genişletilmesi gerektiği anlamına gelmediğini unutmayın, bu da oldukça fazla karakterin düzgün çalışamayacağı anlamına gelir.)


0

İfade regexe kullanın.

Bunun iyi bir örneği, girişi sinüs dalgasına dönüştüren kodu izlemektir:

s/./print" "x(sin($i+=.5)*5+5).$&/eg;

Gördüğünüz gibi, standart girdilerde karakterler üzerinde yineleme yapmanın oldukça kompakt bir yolu. İşlerin nasıl eşleştirildiğini değiştirmek için başka bir regex kullanabilirsiniz.

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.