Consigli per giocare a golf in Perl?


47

Quali consigli generali hai per giocare a golf in Perl? Sto cercando idee che possano essere applicate ai problemi del codice golf in generale che siano almeno in qualche modo specifiche per Perl (ad esempio "rimuovere i commenti" non è una risposta). Si prega di inviare un suggerimento per risposta.

Risposte:


26

TMTOWTDI

Questo è il consiglio per il golf Perl più importante che devi conoscere. Ogni volta che stai osservando una sequenza troppo lunga di personaggi che devi assolutamente avere per svolgere il tuo compito, chiediti se non c'è altro modo per ottenere lo stesso effetto usando una funzione diversa. Di solito c'è. Ecco solo una manciata:

  • ~~applica un contesto scalare ed è 4 caratteri più corto di scalar.

  • y///cè un carattere più corto di lengthquando si ottiene la lunghezza di $_.

  • Hai bisogno di scorrere i caratteri in $_? Sostituisci split//con /./gs. (Oppure usa /./gse vuoi saltare anche le nuove righe.) Funziona con altre variabili: sostituisci split//,$xcon $x=~/./gs.

  • Ogni built-in Perl restituisce qualcosa. printrestituisce 1, ad esempio, per indicare un I / O riuscito. Se è necessario inizializzare $_su un valore vero, ad esempio, $_=print$fooconsente di uccidere due uccelli con una fava.

  • Quasi ogni affermazione in Perl può essere scritta come espressione, permettendone l'utilizzo in una più ampia varietà di contesti. Le dichiarazioni multiple possono diventare espressioni multiple concatenate insieme alle virgole. I test possono essere eseguiti con operatori di cortocircuito ?: && ||e anche con ande or, che fanno la stessa cosa ma con precedenza inferiore rispetto a tutti gli altri operatori (incluso il compito). I loop possono essere eseguiti tramite mapo grep. Anche parole chiave come next, laste returnpuò essere utilizzato in un contesto di espressione, anche se non tornano! Tenere presente questo tipo di trasformazioni ti dà l'opportunità di sostituire i blocchi di codice con espressioni che possono essere inserite in una più ampia varietà di contesti.


$_=print""è più corto di $_=print$foo.
ASCIIThenANSI,

5
L'idea è che è già necessario stampare $foo. Altrimenti, $_=1è molto più breve di $_=print""e ha lo stesso effetto.
breadbox

3
Per il terzo intendi iterare sui caratteri in $x? Altrimenti potresti semplicemente fare /./gse /./g.
redbmk,

25

Abusa delle variabili speciali di Perl!

  • Come osservato nella risposta precedente $/e $"vengono inizializzati di default per "\n"e " ", rispettivamente.

  • $,e $\sono entrambi impostati di undefdefault e sono 3 caratteri più brevi.

  • L'impostazione $\su un valore farà sì che venga aggiunto a tutti print. Ad esempio: perl -ple '$\="=".hex.$/'è un pratico convertitore esadecimale.

  • Se non stai leggendo i file dalla riga di comando, puoi utilizzare l' -iopzione della riga di comando come canale aggiuntivo per inserire una stringa. Il suo valore verrà memorizzato in $^I.

  • $=forza tutto ciò che gli è assegnato per essere un numero intero. Prova a correre perl -ple '$_=$==$_'e dandogli vari inupts. Allo stesso modo, $-impone che il suo valore sia un numero intero non negativo (ovvero un trattino iniziale viene trattato come un carattere non numerico).

  • È possibile utilizzare $.come flag booleano che viene automaticamente reimpostato su un valore vero (diverso da zero) su ogni iterazione di un while(<>)ciclo.


20

-n e parentesi graffe senza pari

È noto che l'opzione della riga di comando -npuò essere utilizzata per eseguire lo script una volta per ogni riga.

perl --help dice:

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

Ciò che non dice esplicitamente è che Perl non si assume semplicemente un ciclo attorno al programma; si avvolge letteralmentewhile (<>) { ... } attorno ad esso.

In questo modo, i seguenti comandi sono equivalenti:

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

-p e parentesi graffe senza pari

Analogamente a quanto sopra, l' -pinterruttore avvolge while (<>) { ... ; print }il programma.

Utilizzando parentesi graffe senza pari, perl -p 'code}{morecode'verrà stampato una sola volta dopo l'esecuzione codeper tutte le righe di input, seguito da morecode.

Poiché $_non è definito quando morecode;printviene eseguito, è $\possibile abusare del separatore del record di output per stampare l'output effettivo.

Per esempio

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

legge un numero per riga da STDIN e stampa la loro somma.


Immagino che potresti farlo con #!perl -nla prima riga, giusto?
ASCIIThenANSI

@ASCIIThenANSI: Sì, è corretto. (Ci scusiamo per la risposta tardiva.)
Dennis

1
Dare credito quando il credito è dovuto, penso di aver visto la combinazione di questi tre trucchi (parentesi graffe senza pari -pe $\​) per la prima volta in una delle risposte Perl di @primo . Leggere le sue risposte è un buon consiglio Perl da solo.
Dennis,

1
Anche gettare una }for(...){tra le parentesi graffe è spesso abbastanza utile, ad esempio codegolf.stackexchange.com/a/25632
primo

Una cosa che ho trovato utile insieme a -n è l'operatore || = valore predefinito. Evita di essere in grado di assegnare un valore prima del ciclo.
deltaray,

18

Utilizzare $_per eliminare i riferimenti scalari. È la variabile speciale che viene utilizzata come impostazione predefinita dalla maggior parte delle funzioni e semplicemente tralasciare i parametri è una scorciatoia per fare riferimento a questa variabile.

Passando $na $_, è possibile passare $n=<>;chop$n;print$na$_=<>;chop;print

Qui, la printfunzione stampa il contenuto di $_default e chopfunziona anche su $_.


È $_=<>;obbligatorio, non <>;legge $_automaticamente la riga ?
Sundar - Ripristina Monica il

No, non la penso così. Ho confrontato i programmi $_=<>;printe <>;print. Il primo mi ripete ciò che scrivo, mentre l'altro no.
PhiNotPi il

Oh, grazie, si scopre che succede solo in casi come print while(<>). Non sono sicuro che si tratti di un caso speciale o che ci sia una logica coerente dietro di esso, né <>la parte perlopné la whileparte di perlsynsembrano menzionare questo comportamento.
Sundar - Ripristina Monica il

1
@sundar while(<>)è un caso speciale, documentato in perlsyn, Operatori I / O: 'Se e solo se il simbolo di input è l'unica cosa all'interno del condizionale di un'istruzione "while" (anche se mascherato da "for (;;)" loop), il valore viene automaticamente assegnato alla variabile globale $ _, distruggendo tutto ciò che c'era in precedenza. "
kernigh

17

Usa le variabili speciali di Perl ovunque tu sia, ad esempio:

  • Usa $"invece di" "
  • Usa $/invece di"\n"

Hanno il vantaggio aggiuntivo di essere un identificatore lungo di un carattere garantito, con l'aiuto del lexer. Ciò consente di incollarlo alla parola chiave che lo segue, come in:print$.for@_

L'elenco di tutte le variabili speciali è disponibile qui: Variabili speciali


15

Non usare qw. Questo è uno spreco di due personaggi che potrebbero essere usati in modo migliore. Ad esempio, non scrivere quanto segue.

@i=qw(unique value);

Usa invece le password.

@i=(unique,value);

Oppure, se non è possibile utilizzare le password, utilizzare la globsintassi.

@i=<unique value>;

glob la sintassi può essere utilizzata anche per effetti interessanti.

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

12

Utilizzare modificatori di istruzioni anziché istruzioni composte.

Le istruzioni composte tendono a richiedere parentesi per l'argomento e parentesi graffe per il blocco, mentre i modificatori di istruzioni non hanno bisogno di nessuno dei due.

Confrontare:

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

Si noti che l'ultimo esempio si collega $c&&chop$,, ma inizia davvero a brillare per la maggior parte delle operazioni multiistruzione. Fondamentalmente tutto ciò che perde la precedenza dell'operatore &&.


11

Non farlo use strict. (non citarmi su questo, il contesto PCG.SE è un po 'importante) E, cosa più importante, non scrivere il codice come se fosse rigoroso. I soliti sospetti:

  • non mydichiarare le variabili se puoi evitarlo. Le uniche variabili di cui myhai veramente bisogno sono quelle che vuoi avere un ambito lessicale. Questo è quasi nessuno di questi quando si gioca a golf, dove non è necessaria la protezione dell'ambito e si tende a controllare completamente la ricorsione.
  • non citare stringhe di una sola parola: ( esempio ). Assicurati però di non avere una funzione con lo stesso nome.

5
print hellonon funzionerà. In realtà significa print hello $_(stampa $_su filehandle hello).
Konrad Borowski il

@GlitchMr grazie! (e ora mi dispiace, perché il mio punto è ancora valido, ma non con print, e ora non riesco a trovare un esempio carino e breve)
JB


11

Sono sicuro che alcuni di questi hanno nomi formali e non ne sono consapevole.

  • Se hai un ciclo while (o un ciclo for che puoi creare in un ciclo while) puoi definire "while" dopo il comando: print $n++ while ($n < 10)
  • Se hai bisogno di leggere tutto da STDIN in una stringa: $var = join('',<>)
  • Come ha sottolineato CeilingSpy, l'uso di $ / invece di \ n è più veloce in alcune situazioni: print ('X'*10) . "\n";è più lungo diprint ('X'*10) . $/;
  • La sayfunzione di Perl è più breve di print, ma dovrai eseguire il codice con -Einvece di-e
  • Usa intervalli come a..zo anche aa..zz. Se necessario come stringa, utilizzare join.
  • Stringhe incrementali: $z = 'z'; print ++$z;verrà visualizzatoaa

Questo è tutto ciò che mi viene in mente in questo momento. Potrei aggiungerne un po 'più tardi.


1
Cosa print ('X'*10) . $/;dovrebbe fare? Per me stampa 0e non newline. Per prima cosa, le parentesi diventano un argomento di chiamata in stile funzione print, che si lega più strettamente di .. E intendevi xinvece *o qualcosa del genere?
aschepler,

Non sono necessarie parentesi in Postfix while, join'',<>;funziona anche senza di esse.
Choroba,

10

Usa caratteri non di parole come nomi di variabili

L'uso di $%invece di $aconsente di posizionare il nome della variabile proprio accanto a un if, foro whilecostruire come in:

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

Molti possono essere usati, ma controlla i documenti e la risposta di @ BreadBox per quali hanno effetti magici!


Usa la mappa quando non puoi usare modificatori di istruzioni

Se non è possibile utilizzare i modificatori di istruzione secondo la risposta di @ JB , la mappa potrebbe salvare un byte:

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

ed è utile se si desidera eseguire iterazioni nidificate in quanto è possibile inserire forloop postfix all'interno di map.


Usa tutte le variabili magiche delle espressioni regolari

Perl ha variabili magiche per "testo prima della corrispondenza" e "testo dopo la corrispondenza", quindi è possibile suddividere in gruppi di due con potenzialmente meno caratteri:

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

Questo potrebbe anche funzionare bene in sostituzione di substr:

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

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

Se hai bisogno del contenuto della partita, $&puoi usarlo, ad esempio:

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

Sostituisci i sottotitoli con nomi lunghi con un nome più breve

Se chiami dire printquattro o più volte nel tuo codice (questo ovviamente varia con la durata della routine che stai chiamando), sostituiscilo con un nome secondario più corto:

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

vs.

print;print;print;print

Sostituire gli incrementatori / decrementatori condizionali

Se hai un codice come:

$i--if$i>0

Puoi usare:

$i-=$i>0

invece per salvare alcuni byte.


Converti in numero intero

Se non stai assegnando a una variabile e quindi non puoi utilizzare il suggerimento di breadbox , puoi usare l'espressione 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

Vale la pena notare, tuttavia, che non è necessario utilizzare un numero intero per accedere a un indice di array:

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

9

redoaggiunge il comportamento del loop a un blocco senza foro while. {redo}è un ciclo infinito.


7

Non parentizzare le chiamate di funzione.

Perl ti permette di chiamare una funzione nota (core o predeclared) usando la NAME LISTsintassi. Ciò ti consente di rilasciare il &sigillo (se lo stavi ancora usando) e le parentesi.

Per esempio: $v=join'',<>

Documentazione completa


5

Prova a utilizzare il valore di un'espressione di assegnazione, in questo modo:

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

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

Questo funziona perché $ncontiene 2 caratteri in Perl. Si può cambiare $nper ()senza alcun costo, e salvare 1 virgola spostando l'assegnazione nelle parentesi.


5

È possibile eseguire più istruzioni diverse all'interno della logica ternaria nidificata.

Supponiamo di avere un grande if- elsifdichiarazione. Questa potrebbe essere qualsiasi logica e qualsiasi numero di affermazioni.

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

È possibile utilizzare (cmd1, cmd2, cmd3)all'interno dell'operatore ternario per eseguire tutti i comandi.

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

Ecco un esempio fittizio:

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

Usa select(undef,undef,undef,$timeout)invece diTime::HiRes

(Tratto da https://stackoverflow.com/a/896928/4739548 )

Molte sfide richiedono di dormire con maggiore precisione rispetto ai numeri interi. select()L'argomento timeout può fare proprio questo.

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

è molto più efficiente di:

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

Il primo ha solo 20 byte, mentre il secondo occupa 39. Tuttavia, il primo richiede che non si stia utilizzando $ue non sia mai stato definito.

Se hai intenzione di usarlo molto, l'importazione Time::HiRespaga, ma se ne hai bisogno solo una volta, usando select($u,$u,$u,0.1)salva 19 byte, il che è sicuramente un miglioramento nella maggior parte dei casi.


1

Riduci le tue dichiarazioni di stampa

A meno che la sfida non specifichi diversamente, non è necessario seguire nuove righe.
La nostra "sfida" dice "emette un numero casuale da 0 a 9 a STDOUT". Possiamo prendere questo codice (28 byte):

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

E abbrevialo in questo (25 byte):

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

semplicemente stampando la variabile. Quest'ultimo vale esclusivamente per questa sfida (19 byte):

print int(rand(10))

ma funziona solo quando non devi fare nulla per la variabile tra assegnazione e stampa.


L'ultimo è già elencato qui .
Sp3000,

@ Sp3000 Grazie, ho aggiornato la mia risposta.
ASCIIThenANSI

1

Usa globs come letterali stringa

Occasionalmente (spesso quando si affrontano sfide di o ) si beneficia notevolmente della capacità di annidare letterali di stringa. Normalmente, lo faresti con q(…). Tuttavia, a seconda dei caratteri necessari nella stringa, potresti essere in grado di salvare un byte e utilizzare <…>l'operatore glob. (Nota che ciò che è racchiuso tra parentesi angolari non può apparire come un filehandle e non sembra che debba essere espanso in un elenco di nomi di file, il che significa che molti personaggi non funzioneranno correttamente.)


0

Usa espressione regexe.

Un'illustrazione decente di questo è il seguente codice che modella l'ingresso in sinusoide:

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

Come puoi vedere, è un modo abbastanza compatto di scorrere i caratteri nell'input standard. Puoi usare altri regex per cambiare il modo in cui le cose vengono abbinate.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.