Tips untuk bermain golf di Perl?

47

Tips umum apa yang Anda miliki untuk bermain golf di Perl? Saya mencari ide yang dapat diterapkan pada masalah kode golf secara umum yang setidaknya agak spesifik untuk Perl (mis. "Hapus komentar" bukan jawaban). Silakan kirim satu tip per jawaban.

komando
sumber

Jawaban:

26

TMTOWTDI

Itulah tip golfing Perl yang paling penting yang perlu Anda ketahui. Setiap kali Anda melihat urutan karakter terlalu lama yang harus Anda miliki untuk menyelesaikan tugas, tanyakan pada diri sendiri apakah tidak ada cara lain untuk mendapatkan efek yang sama menggunakan fitur yang berbeda. Biasanya ada. Ini hanya beberapa:

  • ~~menegakkan konteks skalar dan 4 karakter lebih pendek dari scalar.

  • y///cadalah satu char lebih pendek dari lengthketika mendapatkan panjang $_.

  • Perlu mengulangi karakternya $_? Ganti split//dengan /./gs. (Atau gunakan /./gjika Anda juga ingin melewatkan baris baru.) Ini berfungsi dengan variabel lain: ganti split//,$xdengan $x=~/./gs.

  • Setiap Perl dibangun mengembalikan sesuatu. printmengembalikan 1, misalnya, untuk menunjukkan I / O berhasil. Jika Anda perlu menginisialisasi $_ke nilai sebenarnya, misalnya, $_=print$foomemungkinkan Anda untuk membunuh dua burung dengan satu batu.

  • Hampir setiap pernyataan dalam Perl dapat ditulis sebagai ekspresi, yang memungkinkannya untuk digunakan dalam berbagai konteks yang lebih luas. Beberapa pernyataan dapat menjadi beberapa ekspresi yang dirangkai bersama dengan koma. Pengujian dapat dilakukan dengan operator hubungan arus pendek ?: && ||, dan juga dengan anddan or, yang melakukan hal yang sama tetapi dengan diutamakan lebih rendah dari semua operator lain (termasuk penugasan). Loop dapat dilakukan melalui mapatau grep. Bahkan kata kunci suka next, lastdan returndapat digunakan dalam konteks ekspresi, meskipun tidak kembali! Mempertahankan transformasi semacam ini dalam pikiran memberi Anda peluang untuk mengganti blok kode dengan ekspresi yang dapat dimasukkan ke dalam berbagai konteks yang lebih luas.

kotak roti
sumber
$_=print""lebih pendek dari $_=print$foo.
ASCIIThenANSI
5
Idenya adalah Anda sudah perlu mencetak $foo. Kalau tidak, $_=1jauh lebih pendek daripada $_=print""dan memiliki efek yang sama.
kotak roti
3
Untuk yang ketiga maksud Anda iterasi di chars $x? Kalau tidak, Anda hanya bisa melakukan /./gsdan /./g.
redbmk
25

Variabel khusus penyalahgunaan Perl!

  • Seperti disebutkan dalam jawaban sebelumnya $/dan $"diinisialisasi secara default untuk "\n"dan " ", masing-masing.

  • $,dan $\keduanya diatur undefsecara default, dan 3 karakter lebih pendek.

  • Pengaturan $\ke nilai akan menyebabkannya ditambahkan ke setiap print. Sebagai contoh: perl -ple '$\="=".hex.$/'adalah konverter hex-to-desimal berguna.

  • Jika Anda tidak membaca file dari baris perintah, Anda dapat menggunakan -isaklar baris perintah sebagai saluran tambahan untuk memasukkan string. Nilainya akan disimpan di $^I.

  • $=memaksa apa pun yang ditugaskan padanya untuk menjadi bilangan bulat. Coba jalankan perl -ple '$_=$==$_'dan berikan berbagai inupts. Demikian juga, $-memaksa nilainya menjadi bilangan bulat non-negatif (yaitu tanda hubung utama diperlakukan sebagai karakter non-numerik).

  • Anda dapat menggunakan $.sebagai boolean flag yang secara otomatis di-reset ke nilai true (bukan nol) pada setiap iterasi while(<>)loop.

kotak roti
sumber
20

-n dan kurung keriting yang tak tertandingi

Diketahui bahwa saklar baris perintah -ndapat digunakan untuk mengeksekusi skrip sekali untuk setiap baris.

perl --help mengatakan:

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

Apa yang tidak dikatakannya secara eksplisit adalah bahwa Perl tidak hanya mengasumsikan loop di sekitar program; itu benar - benar membungkusnya while (<>) { ... }.

Dengan cara ini, perintah berikut ini setara satu sama lain:

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

-p dan kurung keriting yang tak tertandingi

Demikian pula dengan yang di atas, -psaklar membungkus while (<>) { ... ; print }program.

Dengan menggunakan kurung keriting yang tidak tertandingi, perl -p 'code}{morecode'hanya akan mencetak sekali setelah mengeksekusi codeuntuk semua jalur input, diikuti oleh morecode.

Karena $_tidak ditentukan saat morecode;printdieksekusi, pemisah catatan output $\dapat disalahgunakan untuk mencetak output yang sebenarnya.

Sebagai contoh

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

membaca satu nomor per baris dari STDIN dan mencetak jumlahnya.

Dennis
sumber
Saya berasumsi Anda bisa mencapai ini dengan #!perl -ndi baris pertama, kan?
ASCIIThenANSI
@ASCIIThenANSI: Ya, itu benar. (Maaf atas jawaban yang terlambat.)
Dennis
1
Memberi kredit di mana kredit jatuh tempo, saya pikir saya melihat kombinasi dari tiga trik ini (kurung keriting yang tak tertandingi, -pdan $\​) untuk pertama kalinya di salah satu jawaban Perl @primo . Untuk membaca jawaban-jawabannya adalah tip Perl yang baik pada dirinya sendiri.
Dennis
1
Melempar }for(...){di sela-sela kawat gigi seringkali cukup berguna juga, misalnya codegolf.stackexchange.com/a/25632
primo
Satu hal yang saya temukan berguna dalam hubungannya dengan -n adalah || = operator penetapan nilai default. Mendapat sekitar tidak dapat menetapkan nilai sebelum loop.
deltaray
18

Gunakan $_untuk menghilangkan referensi skalar. Ini adalah variabel khusus yang digunakan sebagai default oleh sebagian besar fungsi, dan hanya menyisakan parameter adalah jalan pintas untuk referensi variabel ini.

Dengan mengubah $nke $_, Anda dapat mengubah $n=<>;chop$n;print$nke$_=<>;chop;print

Di sini, printfungsi mencetak konten $_secara default, dan chopjuga berfungsi $_.

PhiNotPi
sumber
Apakah $_=<>;wajib, tidak <>;membaca baris ke $_otomatis?
sundar
Tidak, kurasa tidak. Saya membandingkan program $_=<>;printdan <>;print. Yang pertama mengulangi kembali kepada saya apa yang saya ketik, sedangkan yang lain tidak.
PhiNotPi
Oh, terima kasih, ternyata itu hanya terjadi dalam kasus seperti print while(<>). Tidak yakin apakah ini kasus khusus atau ada beberapa logika yang koheren di belakangnya, tidak ada <>bagian perlopatau pun whilebagian yang perlsynmenyebutkan perilaku ini.
sundar
1
@sundar while(<>)adalah kasus khusus, didokumentasikan dalam perlsyn, I / O Operator: 'Jika dan hanya jika simbol input adalah satu-satunya hal di dalam syarat pernyataan "sementara" (bahkan jika disamarkan sebagai "untuk (;;)" loop), nilai tersebut secara otomatis ditetapkan ke variabel global $ _, menghancurkan apa pun yang ada sebelumnya. "
kernigh
17

Gunakan variabel khusus Perl kapan pun Anda bisa, misalnya:

  • Gunakan $"sebagai ganti" "
  • Gunakan $/sebagai ganti"\n"

Mereka memiliki manfaat tambahan sebagai pengidentifikasi panjang satu karakter yang dijamin, dengan bantuan dari lexer. Ini memungkinkan untuk merekatkannya ke kata kunci yang mengikutinya, seperti pada:print$.for@_

Daftar semua variabel khusus tersedia di sini: Variabel Khusus

3aw5TZetdf
sumber
15

Jangan gunakan qw. Ini adalah pemborosan dari dua karakter yang dapat digunakan dengan cara yang lebih baik. Misalnya, jangan menulis yang berikut ini.

@i=qw(unique value);

Alih-alih menggunakan kata kunci.

@i=(unique,value);

Atau jika Anda tidak dapat menggunakan kata kunci, gunakan globsintaks.

@i=<unique value>;

glob sintaksis juga dapat digunakan untuk efek yang menarik.

@i=<item{1,2,3}>;
Konrad Borowski
sumber
12

Gunakan pengubah pernyataan alih-alih pernyataan majemuk.

Pernyataan majemuk cenderung membutuhkan tanda kurung untuk argumen dan kurung untuk blok, sedangkan pengubah pernyataan tidak membutuhkan keduanya.

Membandingkan:

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

Perhatikan bahwa contoh terakhir ada hubungannya $c&&chop$,, tetapi mulai benar-benar bersinar untuk sebagian besar operasi multi-pernyataan. Pada dasarnya apa pun yang kehilangan prioritas operator &&.

JB
sumber
11

Jangan use strict. (jangan mengutip saya tentang ini, konteks PCG.SE agak penting) Dan, yang lebih penting, jangan kode seolah-olah di bawah ketat. Tersangka yang biasa:

  • jangan my-deklarasikan variabel jika Anda bisa menghindarinya. Satu-satunya variabel yang benar-benar dibutuhkan myadalah yang Anda inginkan dicakup secara leksikal. Itu hampir tidak ada dari mereka ketika bermain golf, di mana Anda tidak membutuhkan perlindungan ruang lingkup dan cenderung untuk sepenuhnya mengendalikan rekursi.
  • jangan mengutip string satu kata: ( contoh ). Pastikan Anda tidak memiliki fungsi dengan nama yang sama.
JB
sumber
5
print hellotidak akan bekerja Ini sebenarnya berarti print hello $_(cetak $_ke filehandle hello).
Konrad Borowski
@GlitchMr terima kasih! (dan sekarang saya kecewa, karena poin saya masih valid, hanya saja tidak dengan print, dan sekarang saya tidak dapat menemukan contoh yang bagus dan pendek)
JB
@JB, inilah contoh yang bagus: codegolf.stackexchange.com/a/8746/3348
ardnew
11

Saya yakin beberapa di antaranya memiliki nama resmi dan saya tidak menyadarinya.

  • Jika Anda memiliki loop sementara (atau loop untuk Anda dapat membuat menjadi loop sementara) Anda dapat mendefinisikan "while" setelah perintah: print $n++ while ($n < 10)
  • Jika Anda perlu membaca semuanya dari STDIN menjadi string: $var = join('',<>)
  • Seperti yang ditunjukkan CeilingSpy, menggunakan $ / bukannya \ n lebih cepat dalam beberapa situasi: print ('X'*10) . "\n";lebih lama daripadaprint ('X'*10) . $/;
  • sayFungsi Perl lebih pendek daripada print, tetapi Anda harus menjalankan kode dengan -Ealih - alih-e
  • Gunakan rentang suka a..zatau bahkan aa..zz. Jika diperlukan sebagai string, gunakan join.
  • String yang bertambah: $z = 'z'; print ++$z;akan ditampilkanaa

Hanya itu yang bisa saya pikirkan saat ini. Saya dapat menambahkan lagi nanti.

Tuan Llama
sumber
1
Apa yang print ('X'*10) . $/;seharusnya dilakukan? Bagi saya itu mencetak 0dan tidak ada baris baru. Untuk satu hal, tanda kurung menjadi argumen panggilan fungsi-gaya print, yang mengikat lebih ketat dari .. Dan apakah yang Anda maksudkan xbukan *atau sesuatu?
aschepler
Tidak ada tanda kurung yang diperlukan di postfix while, join'',<>;juga berfungsi tanpa tanda kurung .
choroba
10

Gunakan karakter bukan kata sebagai nama variabel

Menggunakan $%alih-alih $adapat memungkinkan Anda untuk menempatkan nama variabel tepat di sebelah if, foratau whilemembuat seperti pada:

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

Banyak yang dapat digunakan, tetapi periksa dokumen dan jawaban @ BreadBox yang mana yang memiliki efek ajaib!


Gunakan peta saat Anda tidak dapat menggunakan pengubah pernyataan

Jika Anda tidak dapat menggunakan pengubah pernyataan sesuai jawaban @ JB , peta mungkin menyimpan byte:

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

dan berguna jika Anda ingin melakukan iterasi bersarang karena Anda dapat menempatkan forloop postfix di dalam map.


Gunakan semua variabel Ekspresi Reguler sihir

Perl memiliki variabel ajaib untuk 'teks sebelum pertandingan' dan 'teks setelah pertandingan' sehingga dimungkinkan untuk dipecah menjadi kelompok dua dengan karakter yang berpotensi lebih sedikit:

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

Ini juga bisa berfungsi sebagai pengganti untuk substr:

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

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

Jika Anda membutuhkan konten pertandingan, $&dapat digunakan, misalnya:

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

Ganti kapal selam dengan nama panjang dengan nama pendek

Jika Anda menelpon katakan printempat kali atau lebih dalam kode Anda (ini jelas berbeda dengan panjang rutin yang Anda panggil), gantilah dengan sub nama yang lebih pendek:

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

vs.

print;print;print;print

Ganti incrementor / decrementor kondisional

Jika Anda memiliki kode seperti:

$i--if$i>0

Anda dapat gunakan:

$i-=$i>0

alih-alih menyimpan beberapa byte.


Konversikan ke integer

Jika Anda tidak menetapkan ke variabel dan tidak bisa menggunakan tip kotak roti , Anda bisa menggunakan ekspresi 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

Namun perlu dicatat daripada Anda tidak perlu menggunakan integer untuk mengakses indeks array:

@letters = A..Z;
$letters[rand 26]; # random letter
Dom Hastings
sumber
9

redomenambahkan perilaku loop ke blok tanpa foratau while. {redo}adalah loop tak terbatas.

pengguna130144
sumber
7

Jangan kurung panggilan fungsi.

Perl memungkinkan Anda memanggil fungsi yang dikenal (inti atau yang telah dideklarasikan) menggunakan NAME LISTsintaks. Ini memungkinkan Anda menjatuhkan &sigil (jika Anda masih menggunakannya) serta tanda kurung.

Sebagai contoh: $v=join'',<>

Dokumentasi lengkap

JB
sumber
5

Cobalah untuk menggunakan nilai ekspresi penugasan, seperti:

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

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

Ini berfungsi karena $n2 karakter dalam Perl. Anda dapat mengubah $nke ()tanpa biaya, dan menghemat 1 titik koma dengan memindahkan tugas ke dalam tanda kurung.

kernigh
sumber
5

Anda dapat menjalankan beberapa pernyataan berbeda dalam logika terner bersarang.

Misalkan Anda memiliki besar if- elsifpernyataan. Ini bisa berupa logika dan sejumlah pernyataan.

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;
}

Anda dapat menggunakan (cmd1, cmd2, cmd3)di dalam operator ternary untuk menjalankan semua perintah.

$_ < 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

Berikut ini contoh tiruannya:

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)
hmatt1
sumber
4

Gunakan select(undef,undef,undef,$timeout)sebagai gantiTime::HiRes

(Diambil dari https://stackoverflow.com/a/896928/4739548 )

Banyak tantangan yang mengharuskan Anda tidur dengan presisi lebih tinggi daripada bilangan bulat. select()Argumen batas waktu dapat melakukan hal itu.

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

jauh lebih efisien daripada:

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

Yang pertama hanya 20 byte, sedangkan yang terakhir memakan waktu 39. Namun, yang pertama mengharuskan Anda tidak menggunakan $udan tidak pernah mendefinisikannya.

Jika Anda akan menggunakannya, banyak yang diimportensikan Time::HiResterbayar, tetapi jika Anda hanya membutuhkannya sekali saja, menggunakan select($u,$u,$u,0.1)simpanan 19 byte, yang jelas merupakan peningkatan dalam banyak kasus.

ASCIIThenANSI
sumber
1

Persingkat pernyataan cetak Anda

Kecuali jika tantangannya menentukan sebaliknya, Anda tidak perlu membuntuti baris baru.
'Tantangan' kami mengatakan 'mengeluarkan angka acak dari 0 hingga 9 ke STDOUT'. Kita dapat mengambil kode ini (28 byte):

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

Dan persingkat menjadi ini (25 byte):

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

hanya dengan mencetak variabel. Yang terakhir ini hanya berlaku untuk tantangan ini secara khusus (19 byte):

print int(rand(10))

tetapi itu hanya berfungsi ketika Anda tidak perlu melakukan apa pun pada variabel di antara tugas dan pencetakan.

ASCIIThenANSI
sumber
Yang terakhir sudah terdaftar di sini .
Sp3000
@ Sp3000 Terima kasih, saya telah memperbarui jawaban saya.
ASCIIThenANSI
1

Gunakan gumpalan seperti string literal

Kadang-kadang (sering ketika berhadapan dengan atau ) Anda mendapat manfaat besar dari kemampuan untuk bersarang string literal. Biasanya, Anda akan melakukan ini dengan q(…). Namun, tergantung pada karakter apa yang Anda butuhkan di dalam string, Anda mungkin dapat menyimpan byte dan menggunakan <…>, operator glob. (Perhatikan bahwa apa yang ada di dalam kurung sudut tidak dapat terlihat seperti filehandle, dan tidak terlihat seperti itu dimaksudkan untuk diperluas menjadi daftar nama file, yang berarti bahwa cukup banyak karakter tidak akan berfungsi dengan benar.)


sumber
0

Gunakan ekspresi regexe.

Ilustrasi yang layak untuk hal ini adalah kode berikut yang membentuk input menjadi gelombang sinus:

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

Seperti yang Anda lihat, ini cara yang ringkas untuk mengulangi karakter dalam input standar. Anda bisa menggunakan regex lain untuk mengubah cara semuanya dicocokkan.

Krzysztof Szewczyk
sumber