Odpowiedzi:
To najważniejsza wskazówka golfowa Perla, którą musisz wiedzieć. Ilekroć patrzysz na zbyt długą sekwencję znaków, którą absolutnie musisz mieć, aby wykonać swoje zadanie, zadaj sobie pytanie, czy nie ma innego sposobu na uzyskanie tego samego efektu za pomocą innej funkcji. Zwykle jest. Oto tylko garść:
~~
wymusza kontekst skalarny i jest o 4 znaki krótszy niż scalar
.
y///c
jest o jeden char krótszy niż length
przy uzyskiwaniu długości $_
.
Potrzebujesz iteracji po znakach w $_
? Wymień split//
się /./gs
. (Lub użyć /./g
jeśli chcesz pominąć nowej linii.) To działa z innymi zmiennymi zamienić split//,$x
z $x=~/./gs
.
Każde wbudowane Perl coś zwraca. print
zwraca 1, na przykład, aby wskazać udane We / Wy. Jeśli chcesz na przykład zainicjować $_
wartość prawdziwą, $_=print$foo
pozwala zabić dwa ptaki jednym kamieniem.
Prawie każde zdanie w Perlu może być zapisane jako wyrażenie, co pozwala na użycie go w szerszym zakresie kontekstów. Wiele instrukcji może być wieloma wyrażeniami połączonymi łańcuchami z przecinkami. Testy można wykonywać z operatorami zwarciowymi ?:
&&
||
, a także z and
i or
, które robią to samo, ale z pierwszeństwem niższym niż wszystkie inne operatory (w tym przypisanie). Pętle można wykonywać za pomocą map
lub grep
. Nawet jak słowa kluczowe next
, last
i return
może być stosowany w kontekście wypowiedzi, choć nie wracają! Pamiętając o tego rodzaju transformacjach, możesz zastąpić bloki kodu wyrażeniami, które można upchnąć w szerszej gamie kontekstów.
$foo
. W przeciwnym razie $_=1
jest znacznie krótszy niż $_=print""
i ma ten sam efekt.
$x
? W przeciwnym razie możesz po prostu zrobić /./gs
i /./g
.
Nadużywaj specjalnych zmiennych Perla!
Jak zauważono w poprzedniej odpowiedzi $/
i $"
są one domyślnie inicjowane odpowiednio na "\n"
i " "
.
$,
i $\
oba są ustawione undef
domyślnie i są o 3 znaki krótsze.
Ustawienie $\
wartości spowoduje dołączenie jej do każdego print
. Na przykład: perl -ple '$\="=".hex.$/'
jest przydatnym konwerterem szesnastkowym na dziesiętnym.
Jeśli nie czytasz plików z wiersza poleceń, możesz użyć -i
przełącznika wiersza polecenia jako dodatkowego kanału do wprowadzania ciągu. Jego wartość zostanie zapisana w $^I
.
$=
zmusza wszystko, co jest przypisane, do liczby całkowitej. Spróbuj uruchomić perl -ple '$_=$==$_'
i podać różne przeróbki. Podobnie, $-
wymusza, aby jego wartość była nieujemną liczbą całkowitą (tj. Myślnik prowadzący jest traktowany jako znak nienumeryczny).
Można użyć $.
jako flagi logicznej, która jest automatycznie resetowana do wartości true (niezerowej) przy każdej iteracji while(<>)
pętli.
-n
i niedopasowane nawiasy klamroweDobrze wiadomo, że przełącznika wiersza poleceń -n
można użyć do wykonania skryptu raz dla każdej linii.
perl --help
mówi:
-n assume "while (<>) { ... }" loop around program
Nie mówi wprost, że Perl nie zakłada pętli wokół programu; to dosłownie owija while (<>) { ... }
się wokół niego.
W ten sposób następujące polecenia są sobie równe:
perl -e 'while(<>){code}morecode'
perl -ne 'code;END{morecode}'
perl -ne 'code}{morecode'
-p
i niedopasowane nawiasy klamrowePodobnie jak powyżej, -p
przełącznik otacza while (<>) { ... ; print }
program.
Używając niedopasowanych nawiasów klamrowych, perl -p 'code}{morecode'
wydrukuje się tylko raz po wykonaniu code
dla wszystkich wierszy wprowadzania, a następnie morecode
.
Ponieważ $_
podczas morecode;print
wykonywania jest niezdefiniowany , separator rekordów wyjściowych $\
może zostać wykorzystany do wydrukowania rzeczywistych wyników.
Na przykład
perl -pe '$\+=$_}{'
wczytuje z STDIN jedną liczbę w wierszu i wypisuje ich sumę.
#!perl -n
w pierwszej linii, prawda?
}for(...){
się między nawiasami jest często bardzo przydatny, jak również, np codegolf.stackexchange.com/a/25632
Służy $_
do eliminowania odniesień skalarnych. Jest to zmienna specjalna używana domyślnie przez większość funkcji, a pominięcie parametrów jest skrótem do odwołania się do tej zmiennej.
Zmieniając $n
na $_
, możesz zmienić $n=<>;chop$n;print$n
na$_=<>;chop;print
Tutaj print
funkcja $_
domyślnie drukuje zawartość , a chop
także działa na $_
.
$_=<>;
wymagany, nie <>;
czyta wiersza $_
automatycznie?
$_=<>;print
i <>;print
. Pierwszy powtarza mi to, co piszę, a drugi nie.
print while(<>)
. Nie jestem pewien, czy jest to szczególny przypadek czy istnieje jakaś spójna logika za nim, ani <>
nic w części perlop
ani while
częścią perlsyn
wydają wspomnieć to zachowanie.
while(<>)
to szczególny przypadek, udokumentowany w perlsyn, operatory I / O: „Tylko wtedy, gdy symbol wejściowy jest jedyną rzeczą w warunku instrukcji„ while ”(nawet jeśli jest zamaskowany jako„ for (;;) ” pętla), wartość jest automatycznie przypisywana do zmiennej globalnej $ _, niszcząc wszystko, co było wcześniej. ”
Używaj specjalnych zmiennych Perla, gdy tylko możesz, np .:
$"
zamiast" "
$/
zamiast"\n"
Mają dodatkową zaletę, że są gwarantowanym jednoznakowym długim identyfikatorem, z pomocą leksykonu. Umożliwia to przyklejenie go do następującego po nim słowa kluczowego, jak w:print$.for@_
Lista wszystkich zmiennych specjalnych jest dostępna tutaj: Zmienne specjalne
Nie używać qw
. To marnotrawstwo dwóch postaci, które można by lepiej wykorzystać. Na przykład nie pisz tego.
@i=qw(unique value);
Zamiast tego używaj prostych słów.
@i=(unique,value);
Lub jeśli nie możesz używać słów bez słów, użyj glob
składni.
@i=<unique value>;
glob
składnia może być również wykorzystana do uzyskania interesujących efektów.
@i=<item{1,2,3}>;
Użyj modyfikatorów instrukcji zamiast instrukcji złożonych.
Instrukcje złożone zwykle wymagają nawiasów dla argumentu i nawiasów klamrowych dla bloku, podczas gdy modyfikatory instrukcji nie potrzebują tego.
Porównać:
$a++,$b++while$n--
vs while($n--){$a++;$b++}
chop$,if$c
vs if($c){chop$,}
Zauważ, że ostatni przykład wiąże się z $c&&chop$,
, ale zaczyna naprawdę świecić w przypadku większości operacji na wielu instrukcjach. Zasadniczo wszystko, co traci pierwszeństwo operatora &&
.
Nie robić use strict
. (nie cytuj mnie, kontekst PCG.SE ma znaczenie) I, co ważniejsze, nie koduj tak, jakby był ściśle określony. Podejrzani:
my
usuwaj zmiennych, jeśli możesz tego uniknąć. Jedyne zmienne, które naprawdę potrzebują, my
to te, które mają zasięg leksykalny. To prawie żaden z nich podczas gry w golfa, gdzie nie potrzebujesz ochrony lunety i masz tendencję do pełnej kontroli rekurencji.print hello
nie zadziała. W rzeczywistości oznacza to print hello $_
(drukuj $_
do uchwytu pliku hello
).
print
, a teraz nie mogę znaleźć ładnego i krótkiego przykładu)
Jestem pewien, że niektóre z nich mają formalne nazwy i po prostu ich nie znam.
print $n++ while ($n < 10)
$var = join('',<>)
print ('X'*10) . "\n";
jest dłuższe niżprint ('X'*10) . $/;
say
Funkcja Perla jest krótsza niż print
, ale będziesz musiał uruchomić kod -E
zamiast-e
a..z
lub nawet aa..zz
. W razie potrzeby użyj ciągu join
.$z = 'z'; print ++$z;
wyświetli sięaa
To wszystko, co mogę teraz wymyślić. Mogę dodać trochę później.
print ('X'*10) . $/;
ma robić? Dla mnie to drukuje 0
i nie ma nowej linii. Po pierwsze, nawiasy stają się argumentem wywołania w stylu funkcji do print
, który wiąże się mocniej niż .
. A miałeś na myśli x
zamiast *
czy coś?
while
, join'',<>;
działa również bez nich.
Korzystanie $%
zamiast $a
może zezwolić, aby umieścić nazwę zmiennej tuż obok if
, for
albo while
skonstruować na przykład:
@r=(1,2,3,4,5);$%=4;
print$_*$%for@r
Można użyć wielu, ale sprawdź dokumentację i odpowiedź @ BreadBox, które mają magiczne efekty!
Jeśli nie możesz użyć modyfikatorów instrukcji zgodnie z odpowiedzią @ JB , mapa może zapisać bajt:
for(@c){}
vs. map{}@c;
i jest przydatny, jeśli chcesz wykonywać zagnieżdżone iteracje, ponieważ możesz umieścić w nim for
pętle Postfiksa map
.
Perl ma magiczne zmienne dla „tekstu przed dopasowaniem” i „tekstu po dopasowaniu”, więc możliwe jest podzielenie na grupy dwuosobowe o potencjalnie mniejszej liczbie znaków:
($x,$y)=split/,/,$_;
($x,$y)=/(.+),(.+)/;
/,/; # $x=$`, $y=$'
# Note: you will need to save the variables if you'll be using more regex matches!
Może to również działać dobrze jako zamiennik dla substr
:
$s=substr$_,1;
/./;# $s=$'
$s=substr$_,4;
/.{4}/;# $s=$'
Jeśli potrzebujesz zawartości meczu, $&
możesz użyć np .:
# assume input like '10 PRINT "Hello, World!"'
($n,$c,$x)=split/ /,$_;
/ .+ /; # $n=$`, $c=$&, $x=$'
Jeśli dzwonisz print
w kodzie powiedz co najmniej cztery razy (to oczywiście różni się w zależności od długości procedury, którą wywołujesz), zastąp go krótszą nazwą podrzędną:
sub p{print@_}p;p;p;p
vs.
print;print;print;print
Jeśli masz kod taki jak:
$i--if$i>0
możesz użyć:
$i-=$i>0
zamiast tego, aby zapisać niektóre bajty.
Jeśli nie przypisujesz zmiennej, więc nie możesz użyć końcówki chleba , możesz użyć wyrażenia 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
Warto jednak zauważyć, że nie trzeba używać liczby całkowitej, aby uzyskać dostęp do indeksu tablicy:
@letters = A..Z;
$letters[rand 26]; # random letter
redo
dodaje zachowanie pętli do bloku bez for
lub while
. {redo}
jest nieskończoną pętlą.
Nie nawiasuj wywołań funkcji.
Perl pozwala wywoływać znaną (podstawową lub zadeklarowaną) funkcję przy użyciu NAME LIST
składni. Pozwala to upuścić &
sigil (jeśli nadal go używasz), a także nawiasy.
Na przykład: $v=join'',<>
Możesz uruchomić wiele różnych instrukcji w zagnieżdżonej logice trójskładnikowej.
Załóżmy, że masz duży if
- elsif
oświadczenie. Może to być dowolna logika i dowolna liczba instrukcji.
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;
}
Możesz użyć (cmd1, cmd2, cmd3)
wewnątrz operatora trójskładnikowego, aby uruchomić wszystkie polecenia.
$_ < 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
Oto fałszywy przykład:
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)
select(undef,undef,undef,$timeout)
zamiastTime::HiRes
(Źródło: https://stackoverflow.com/a/896928/4739548 )
Wiele wyzwań wymaga spania z większą precyzją niż liczby całkowite. select()
Argument limitu czasu może to zrobić.
select($u,$u,$u,0.1)
jest znacznie bardziej wydajny niż:
import Time::HiRes qw(sleep);sleep(0.1)
Pierwszy z nich ma tylko 20 bajtów, podczas gdy drugi zajmuje 39. Jednak pierwszy wymaga, abyś nie używał $u
i nigdy go nie zdefiniował.
Jeśli zamierzasz go używać, importowanie się Time::HiRes
opłaca, ale jeśli potrzebujesz go tylko raz, użycie select($u,$u,$u,0.1)
oszczędza 19 bajtów, co jest zdecydowanie poprawą w większości przypadków.
O ile wyzwanie nie stanowi inaczej, nie potrzebujesz końcowych znaków nowej linii.
Nasze „wyzwanie” mówi „wypisz losową liczbę od 0 do 9 do STDOUT”. Możemy pobrać ten kod (28 bajtów):
$s=int(rand(10));print"$s\n"
I skróć go do tego (25 bajtów):
$s=int(rand(10));print $s
po prostu drukując zmienną. Ten ostatni dotyczy tylko tego wyzwania (19 bajtów):
print int(rand(10))
ale działa to tylko wtedy, gdy nie musisz nic robić ze zmienną między przypisaniem a drukowaniem.
Czasami (często podczas rozwiązywania problemów z quine lub ograniczonymi źródłami ) bardzo zyskujesz na możliwości zagnieżdżania literałów łańcuchowych. Zwykle zrobiłbyś to za pomocą q(…)
. Jednak w zależności od potrzebnych znaków w ciągu możesz zapisać bajt i użyć <…>
operatora glob. (Zauważ, że to, co znajduje się w nawiasach kątowych, nie może wyglądać jak uchwyt pliku i nie może wyglądać, jakby miało być rozwinięte do listy nazw plików, co oznacza, że całkiem sporo znaków nie będzie działać poprawnie.)
Przyzwoitą ilustracją tego jest następujący kod kształtujący sygnał wejściowy w fali sinusoidalnej:
s/./print" "x(sin($i+=.5)*5+5).$&/eg;
Jak widać, jest to dość kompaktowy sposób iteracji znaków w standardowym wprowadzaniu. Możesz użyć innego wyrażenia regularnego, aby zmienić sposób dopasowania rzeczy.
$_=print""
jest krótszy niż$_=print$foo
.