Wskazówki dotyczące gry w golfa w Perlu?


47

Jakie masz ogólne wskazówki na temat gry w golfa w Perlu? Szukam pomysłów, które można by zastosować do ogólnych problemów z golfem, które są przynajmniej nieco specyficzne dla Perla (np. „Usuń komentarze” nie jest odpowiedzią). Proszę zamieścić jedną wskazówkę na odpowiedź.

Odpowiedzi:


26

TMTOWTDI

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///cjest o jeden char krótszy niż lengthprzy uzyskiwaniu długości $_.

  • Potrzebujesz iteracji po znakach w $_? Wymień split//się /./gs. (Lub użyć /./gjeśli chcesz pominąć nowej linii.) To działa z innymi zmiennymi zamienić split//,$xz $x=~/./gs.

  • Każde wbudowane Perl coś zwraca. printzwraca 1, na przykład, aby wskazać udane We / Wy. Jeśli chcesz na przykład zainicjować $_wartość prawdziwą, $_=print$foopozwala 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 andi 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ą maplub grep. Nawet jak słowa kluczowe next, lasti returnmoż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.


$_=print""jest krótszy niż $_=print$foo.
ASCIIThenANSI,

5
Chodzi o to, że musisz już wydrukować $foo. W przeciwnym razie $_=1jest znacznie krótszy niż $_=print""i ma ten sam efekt.
breadbox

3
Po trzecie, masz na myśli iterację po znakach $x? W przeciwnym razie możesz po prostu zrobić /./gsi /./g.
redbmk

25

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 undefdomyś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ć -iprzełą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.


20

-n i niedopasowane nawiasy klamrowe

Dobrze wiadomo, że przełącznika wiersza poleceń -nmoż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 klamrowe

Podobnie jak powyżej, -pprzełącznik otacza while (<>) { ... ; print }program.

Używając niedopasowanych nawiasów klamrowych, perl -p 'code}{morecode'wydrukuje się tylko raz po wykonaniu codedla wszystkich wierszy wprowadzania, a następnie morecode.

Ponieważ $_podczas morecode;printwykonywania 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ę.


Zakładam, że mógłbyś to osiągnąć #!perl -nw pierwszej linii, prawda?
ASCIIThenANSI

@ASCIIThenANSI: Tak, to prawda. (Przepraszam za spóźnioną odpowiedź.)
Dennis

1
Uznając, że należy się uznanie, myślę, że po raz pierwszy zobaczyłem kombinację tych trzech sztuczek (niedopasowane nawiasy klamrowe -pi $\​) w jednej z odpowiedzi Perla @primo . Samo czytanie jego odpowiedzi jest dobrą wskazówką dla Perla.
Dennis

1
Rzucanie }for(...){się między nawiasami jest często bardzo przydatny, jak również, np codegolf.stackexchange.com/a/25632
primo

Jedną z rzeczy, które uznałem za przydatne w połączeniu z -n, jest operator przypisania wartości domyślnej || =. Powoduje, że nie można przypisać wartości przed pętlą.
deltaray

18

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 $nna $_, możesz zmienić $n=<>;chop$n;print$nna$_=<>;chop;print

Tutaj printfunkcja $_domyślnie drukuje zawartość , a choptakże działa na $_.


Jest $_=<>;wymagany, nie <>;czyta wiersza $_automatycznie?
sundar

Nie, nie sądzę. Porównałem programy $_=<>;printi <>;print. Pierwszy powtarza mi to, co piszę, a drugi nie.
PhiNotPi

Och, dzięki, okazuje się, że dzieje się to tylko w takich przypadkach print while(<>). Nie jestem pewien, czy jest to szczególny przypadek czy istnieje jakaś spójna logika za nim, ani <>nic w części perlopani whileczęścią perlsynwydają wspomnieć to zachowanie.
Niedziela

1
@sundar 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. ”
kernigh

17

Używaj specjalnych zmiennych Perla, gdy tylko możesz, np .:

  • Użyj $"zamiast" "
  • Użyj $/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


15

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 globskładni.

@i=<unique value>;

glob składnia może być również wykorzystana do uzyskania interesujących efektów.

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

12

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 &&.


11

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:

  • nie myusuwaj zmiennych, jeśli możesz tego uniknąć. Jedyne zmienne, które naprawdę potrzebują, myto 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.
  • nie cytuj ciągów jednowyrazowych: ( przykład ). Upewnij się jednak, że nie masz funkcji o tej samej nazwie.

5
print hellonie zadziała. W rzeczywistości oznacza to print hello $_(drukuj $_do uchwytu pliku hello).
Konrad Borowski

@GlitchMr dzięki! (a teraz jestem zbity z tropu, ponieważ mój punkt widzenia jest nadal aktualny, po prostu nie print, a teraz nie mogę znaleźć ładnego i krótkiego przykładu)
JB


11

Jestem pewien, że niektóre z nich mają formalne nazwy i po prostu ich nie znam.

  • Jeśli masz pętlę while (lub pętlę for, którą możesz przekształcić w pętlę while), po komendzie możesz zdefiniować „while”: print $n++ while ($n < 10)
  • Jeśli chcesz przeczytać wszystko ze STDIN na ciąg: $var = join('',<>)
  • Jak wskazał CeilingSpy, użycie $ / zamiast \ n jest szybsze w niektórych sytuacjach: print ('X'*10) . "\n";jest dłuższe niżprint ('X'*10) . $/;
  • sayFunkcja Perla jest krótsza niż print, ale będziesz musiał uruchomić kod -Ezamiast-e
  • Użyj zakresów takich jak a..zlub nawet aa..zz. W razie potrzeby użyj ciągu join.
  • Przyrosty ciągów: $z = 'z'; print ++$z;wyświetli sięaa

To wszystko, co mogę teraz wymyślić. Mogę dodać trochę później.


1
Co print ('X'*10) . $/;ma robić? Dla mnie to drukuje 0i 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 xzamiast *czy coś?
aschepler

W postfiksie nie są potrzebne nawiasy while, join'',<>;działa również bez nich.
choroba

10

Używaj znaków innych niż słowa jako nazwy zmiennych

Korzystanie $%zamiast $amoże zezwolić, aby umieścić nazwę zmiennej tuż obok if, foralbo whileskonstruować 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!


Użyj mapy, gdy nie możesz użyć modyfikatorów instrukcji

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 forpętle Postfiksa map.


Użyj wszystkich magicznych zmiennych wyrażeń regularnych

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=$'

Zamień napisy na długie nazwy na krótsze

Jeśli dzwonisz printw 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

Wymień warunkowe inkrementatory / dekrementory

Jeśli masz kod taki jak:

$i--if$i>0

możesz użyć:

$i-=$i>0

zamiast tego, aby zapisać niektóre bajty.


Konwertuj na liczbę całkowitą

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


7

Nie nawiasuj wywołań funkcji.

Perl pozwala wywoływać znaną (podstawową lub zadeklarowaną) funkcję przy użyciu NAME LISTskładni. Pozwala to upuścić &sigil (jeśli nadal go używasz), a także nawiasy.

Na przykład: $v=join'',<>

Pełna dokumentacja


5

Spróbuj użyć wartości wyrażenia przypisania, na przykład:

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

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

Działa $nto, ponieważ w Perlu są 2 znaki. Można zmienić $n, aby ()bez żadnych kosztów i zapisywać 1 średnik przesuwając zadanie w nawiasach.


5

Możesz uruchomić wiele różnych instrukcji w zagnieżdżonej logice trójskładnikowej.

Załóżmy, że masz duży if- elsifoś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)

4

Użyj 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ł $ui nigdy go nie zdefiniował.

Jeśli zamierzasz go używać, importowanie się Time::HiResopł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.


1

Skróć swoje drukowane wyciągi

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.


Ostatni jest już tutaj wymieniony .
Sp3000

@ Sp3000 Dziękuję, zaktualizowałem swoją odpowiedź.
ASCIIThenANSI

1

Używaj globów jak literałów łańcuchowych

Czasami (często podczas rozwiązywania z lub ) 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.)


0

Użyj wyrażenia regularnego.

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.