Des astuces pour jouer au golf à Perl?


47

Quels conseils généraux avez-vous pour jouer au golf à Perl? Je recherche des idées pouvant être appliquées aux problèmes de code de golf en général, qui sont au moins quelque peu spécifiques à Perl (par exemple, "supprimer les commentaires" n'est pas une réponse). Merci de poster un pourboire par réponse.

Réponses:


26

TMTOWTDI

C'est le conseil de golf Perl le plus important que vous devez connaître. Chaque fois que vous regardez une séquence de caractères trop longue que vous devez absolument posséder pour pouvoir accomplir votre tâche, demandez-vous s'il n'existe pas d'autre moyen d'obtenir le même effet en utilisant une fonctionnalité différente. Il y a habituellement. Voici juste une poignée:

  • ~~applique un contexte scalaire et est plus court que 4 caractères scalar.

  • y///cest un caractère plus court que lengthpour obtenir la longueur de $_.

  • Besoin de parcourir les caractères dans $_? Remplacer split//par /./gs. (Ou utilisez /./gsi vous souhaitez également ignorer les nouvelles lignes.) Cela fonctionne avec d'autres variables: remplacez split//,$xpar $x=~/./gs.

  • Chaque Perl intégré renvoie quelque chose. printrenvoie 1, par exemple, pour indiquer une entrée / sortie réussie. Si vous devez initialiser $_à une valeur vraie, par exemple, $_=print$foovous permet de tuer deux oiseaux avec une pierre.

  • Presque toutes les déclarations en Perl peuvent être écrites comme une expression, ce qui permet de les utiliser dans une plus grande variété de contextes. Plusieurs déclarations peuvent devenir plusieurs expressions chaînées avec des virgules. Les tests peuvent être effectués avec des opérateurs de court-circuit ?: && ||, mais également avec andet or, qui font la même chose, mais avec une priorité inférieure à celle de tous les autres opérateurs (y compris l'affectation). Les boucles peuvent être effectuées via mapou grep. Même des mots clés tels que next, lastet returnpeuvent être utilisés dans un contexte d'expression, même s'ils ne sont pas renvoyés! En gardant à l'esprit ce type de transformations, vous pouvez remplacer les blocs de code par des expressions pouvant être intégrées à une plus grande variété de contextes.


$_=print""est plus court que $_=print$foo.
ASCIIThenANSI

5
L'idée est que vous devez déjà imprimer $foo. Sinon, $_=1est beaucoup plus courte que $_=print""et a le même effet.
breadbox

3
Pour le troisième, vous voulez dire itérer sur les caractères $x? Sinon, vous pourriez simplement faire /./gset /./g.
Redbmk

25

Abuser des variables spéciales de Perl!

  • Comme indiqué dans une réponse précédente $/, ils $"sont initialisés par défaut à "\n"et " ", respectivement.

  • $,et $\sont tous deux réglés undefpar défaut et ont 3 caractères plus courts.

  • Si $\vous définissez une valeur, elle sera ajoutée à chaque print. Par exemple: perl -ple '$\="=".hex.$/'est un convertisseur hexadécimal très pratique.

  • Si vous ne lisez pas de fichiers à partir de la ligne de commande, vous pouvez utiliser le -icommutateur de ligne de commande comme canal supplémentaire pour la saisie d'une chaîne. Sa valeur sera stockée dans $^I.

  • $=oblige tout ce qui lui est assigné à être un entier. Essayez de courir perl -ple '$_=$==$_'et de lui donner divers problèmes. De même, $-force sa valeur à être un entier non négatif (c’est-à-dire qu’un tiret est traité comme un caractère non numérique).

  • Vous pouvez utiliser $.un indicateur booléen qui est automatiquement réinitialisé sur une valeur vraie (différente de zéro) à chaque itération d'une while(<>)boucle.


20

-n et accolades inégalées

Il est bien connu que le commutateur de ligne de commande -npeut être utilisé pour exécuter le script une fois pour chaque ligne.

perl --help dit:

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

Ce que cela ne dit pas explicitement, c'est que Perl n'assume pas simplement une boucle autour du programme; il s'enroule littéralementwhile (<>) { ... } autour de lui.

De cette façon, les commandes suivantes sont équivalentes:

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

-p et accolades inégalées

De même que ci-dessus, le -pcommutateur while (<>) { ... ; print }entoure le programme.

En utilisant des accolades sans correspondance, perl -p 'code}{morecode'n’imprimera qu’une fois après avoir été exécuté codepour toutes les lignes d’entrée, suivi de morecode.

Comme $_indéfini quand morecode;printest exécuté, le séparateur d'enregistrement de sortie $\peut être utilisé pour imprimer la sortie réelle.

Par exemple

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

lit un numéro par ligne à partir de STDIN et imprime sa somme.


Je suppose que vous pouvez accomplir cela avec #!perl -nla première ligne, non?
ASCIIThenANSI

@ASCIIThenANSI: Oui, c'est correct. (Désolé pour la réponse tardive.)
Dennis

1
Donner crédit lorsque le crédit est dû, je pense avoir vu la combinaison de ces trois astuces (des accolades incomparables -pet $\​) pour la première fois dans une des réponses Perl de @primo . Lire ses réponses est un bon conseil Perl en soi.
Dennis

1
Jeter un }for(...){entre les accolades est souvent très pratique aussi, par exemple codegolf.stackexchange.com/a/25632
primo

Une chose que j'ai trouvée utile avec -n est l'opérateur d'attribution de valeur || = par défaut. Contourner le fait de ne pouvoir attribuer une valeur avant la boucle.
Deltaray

18

Utilisez $_pour éliminer les références scalaires. Il s’agit de la variable spéciale utilisée par défaut par la plupart des fonctions et le fait de laisser des paramètres de côté est un raccourci pour référencer cette variable.

En changeant $nà $_, vous pouvez changer $n=<>;chop$n;print$nen$_=<>;chop;print

Ici, la printfonction imprime le contenu de $_par défaut et chopfonctionne également $_.


Est $_=<>;requis, ne <>;lit pas la ligne $_automatiquement?
dimanche

Non je ne pense pas. J'ai comparé les programmes $_=<>;printet <>;print. Le premier me répète ce que je tape, alors que l'autre ne le fait pas.
PhiNotPi

Oh, merci, cela ne se produit que dans des cas similaires print while(<>). Je ne sais pas si c'est un cas particulier ou il y a une certaine logique cohérente derrière elle, ni <>fait partie de dans , perlopni une whilepartie de perlsynsemblent parler de ce comportement.
dimanche

1
@sundar while(<>)est un cas particulier, documenté dans perlsyn, Opérateurs d'E / S: 'Si et seulement si le symbole d'entrée est la seule chose dans le conditionnel d'une instruction "while" (même si déguisé en "for (;;)" boucle), la valeur est automatiquement affectée à la variable globale $ _, détruisant tout ce qui s'y trouvait auparavant. "
kernigh

17

Utilisez les variables spéciales de Perl chaque fois que vous le pouvez, par exemple:

  • Utiliser $"au lieu de" "
  • Utiliser $/au lieu de"\n"

Ils ont l’avantage supplémentaire d’être un identifiant long garanti d’un caractère, avec l’aide du lexer. Cela permet de le coller au mot clé qui le suit, comme dans:print$.for@_

La liste de toutes les variables spéciales est disponible ici: Variables spéciales


15

Ne pas utiliser qw. C'est un gaspillage de deux personnages qui pourraient être utilisés de manière plus efficace. Par exemple, n'écrivez pas ce qui suit.

@i=qw(unique value);

Utilisez plutôt des mots nus.

@i=(unique,value);

Ou si vous ne pouvez pas utiliser de mots nus, utilisez la globsyntaxe.

@i=<unique value>;

glob la syntaxe peut également être utilisée pour des effets intéressants.

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

12

Utilisez des modificateurs d'instruction au lieu d'instructions composées.

Les instructions composées ont tendance à exiger des parenthèses pour l'argument et des accolades pour le bloc, alors que les modificateurs d'instruction ne nécessitent ni l'un ni l'autre.

Comparer:

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

Notez que le dernier exemple est lié à $c&&chop$,, mais commence vraiment à briller pour la plupart des opérations multi-instructions. Fondamentalement, tout ce qui fait perdre la priorité à l'opérateur &&.


11

Ne pas use strict. (Ne me citez pas à ce sujet, le contexte de PCG.SE est un peu important) et, plus important encore, ne codez pas comme si vous étiez sous stricte. Les suspects habituels:

  • ne my-déclare pas les variables si tu peux l'éviter. Les seules variables dont myvous avez réellement besoin sont celles que vous souhaitez définir de manière lexicale. C’est à peine si vous jouez au golf, pour lequel vous n’avez pas besoin de protection et qui ont tendance à contrôler totalement la récursivité.
  • ne citez pas de chaînes d'un mot: ( exemple ). Assurez-vous de ne pas avoir une fonction portant le même nom, cependant.

5
print hellone fonctionnera pas. Cela signifie réellement print hello $_(print $_to filehandle hello).
Konrad Borowski

@ GlitchMr merci! (et maintenant je suis déçu, parce que mon point est toujours valable, mais pas avec print, et maintenant je ne trouve pas un exemple court et agréable)
JB

@JB en voici un bon exemple: codegolf.stackexchange.com/a/8746/3348
ardnew

11

Je suis sûr que certains d'entre eux ont des noms officiels et je ne les connais tout simplement pas.

  • Si vous avez une boucle while (ou une boucle for que vous pouvez transformer en boucle while), vous pouvez définir le "while" après la commande: print $n++ while ($n < 10)
  • Si vous devez tout lire depuis STDIN dans une chaîne: $var = join('',<>)
  • Comme CeilingSpy l’a souligné, utiliser $ / au lieu de \ n est plus rapide dans certaines situations: print ('X'*10) . "\n";est plus long queprint ('X'*10) . $/;
  • La sayfonction de Perl est plus courte que print, mais vous devrez exécuter le code avec -Eau lieu de-e
  • Utilisez des gammes comme a..zou même aa..zz. Si nécessaire en tant que chaîne, utilisez join.
  • Incrémentation des chaînes: $z = 'z'; print ++$z;afficheraaa

C'est tout ce à quoi je peux penser maintenant. J'en ajouterai peut-être plus tard.


1
Qu'est-ce qu'on est print ('X'*10) . $/;censé faire? Pour moi, il imprime 0et pas de nouvelle ligne. D'une part, les parenthèses deviennent un argument d'appel de style de fonction print, qui lie plus étroitement que .. Et avez-vous voulu dire xau lieu de *ou quelque chose?
Aschepler

Aucune parenthèse n'est nécessaire dans postfix while, cela join'',<>;fonctionne aussi sans elles.
Choroba

10

Utilisez des caractères autres que des mots comme noms de variables

Utiliser $%au lieu de $apeut vous permettre de placer le nom de variable juste à côté d'un if, forou de whileconstruire comme dans:

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

Beaucoup peuvent être utilisés, mais vérifiez la documentation et la réponse de @ BreadBox pour savoir lesquelles ont des effets magiques!


Utiliser la carte lorsque vous ne pouvez pas utiliser les modificateurs d'instruction

Si vous ne pouvez pas utiliser l'instruction modfier selon la réponse de @ JB , map peut enregistrer un octet:

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

et est utile si vous voulez faire des itérations imbriquées car vous pouvez mettre des forboucles postfix dans le map.


Utilisez toutes les variables magiques des expressions régulières

Perl a des variables magiques pour 'texte avant correspondance' et 'texte après correspondance', il est donc possible de scinder en groupes de deux avec potentiellement moins de caractères:

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

Cela pourrait également bien fonctionner en remplacement de substr:

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

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

Si vous avez besoin du contenu de la correspondance, vous $&pouvez l'utiliser, par exemple:

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

Remplacer les sous-noms par des noms longs par des noms plus courts

Si vous appelez printplusieurs fois dans votre code (cela dépend évidemment de la durée de la routine que vous appelez), remplacez-le par un nom de sous-nom plus court:

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

contre.

print;print;print;print

Remplacer les incrémenteurs / décrémenteurs conditionnels

Si vous avez un code comme:

$i--if$i>0

vous pouvez utiliser:

$i-=$i>0

au lieu de sauver quelques octets.


Convertir en entier

Si vous n'affectez pas de variable et ne pouvez donc pas utiliser le conseil de breadbox , vous pouvez utiliser l'expression 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

Il est à noter cependant que vous n'avez pas besoin d'utiliser un entier pour accéder à un index de tableau:

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

9

redoajoute le comportement de la boucle à un bloc sans forou while. {redo}est une boucle infinie.


7

Ne mettez pas entre parenthèses les appels de fonction.

Perl vous permet d'appeler une fonction connue (principale ou prédéclarée) à l'aide de la NAME LISTsyntaxe. Cela vous permet de supprimer le &sigil (si vous l'utilisiez encore) ainsi que les parenthèses.

Par exemple: $v=join'',<>

Documentation complète


5

Essayez d'utiliser la valeur d'une expression d'affectation, comme suit:

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

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

Cela fonctionne car il $ny a 2 caractères en Perl. Vous pouvez changer $nà ()sans frais et enregistrer 1 virgule en déplaçant l'affectation dans les parenthèses.


5

Vous pouvez exécuter plusieurs instructions différentes dans la logique ternaire imbriquée.

Supposons que vous ayez une grande if- elsifdéclaration. Cela pourrait être n'importe quelle logique et n'importe quel nombre d'énoncés.

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

Vous pouvez utiliser (cmd1, cmd2, cmd3)l'opérateur ternaire pour exécuter toutes les commandes.

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

Voici un exemple factice:

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

Utiliser select(undef,undef,undef,$timeout)au lieu deTime::HiRes

(Tiré de https://stackoverflow.com/a/896928/4739548 )

De nombreux défis vous obligent à dormir avec une précision supérieure à celle des nombres entiers. select()L'argument de délai d'attente peut faire exactement cela.

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

est beaucoup plus efficace que:

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

Le premier ne fait que 20 octets, alors que le dernier en occupe 39. Cependant, le premier exige que vous n'utilisiez pas $uet que vous ne l'ayez jamais défini.

Si vous comptez vous en servir, l'importation vous Time::HiResrapporte beaucoup , mais si vous n'en avez besoin qu'une seule fois, vous select($u,$u,$u,0.1)économisez 19 octets, ce qui est certainement une amélioration dans la plupart des cas.


1

Raccourcissez vos relevés d'impression

Sauf indication contraire du défi, vous n'avez pas besoin de nouvelles lignes.
Notre "défi" dit "affiche un nombre aléatoire de 0 à 9 pour STDOUT". Nous pouvons prendre ce code (28 octets):

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

Et raccourcissez-le à ceci (25 octets):

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

en imprimant simplement la variable. Ce dernier s'applique uniquement à ce défi (19 octets):

print int(rand(10))

mais cela ne fonctionne que lorsque vous n'avez rien à faire avec la variable entre affectation et impression.


Le dernier est déjà répertorié ici .
Sp3000

@ Sp3000 Merci, j'ai mis à jour ma réponse.
ASCIIThenANSI

1

Utilisez des globes comme des littéraux de chaîne

De temps en temps (souvent lorsqu’il s’agit de problèmes de ou ), vous bénéficiez grandement de la possibilité d’imbriquer des littéraux de chaîne. Normalement, vous le feriez avec q(…). Toutefois, en fonction des caractères dont vous avez besoin dans la chaîne, vous pourrez peut-être enregistrer un octet et utiliser <…>l'opérateur glob. (Notez que ce qui se trouve à l'intérieur des crochets n'apparaît pas comme un descripteur de fichier, ni comme il est censé être étendu à une liste de noms de fichiers, ce qui signifie qu'un grand nombre de caractères ne fonctionneront pas correctement.)


0

Utilisez l'expression regexe.

Une illustration décente de ceci est le code suivant façonnant l’entrée en onde sinusoïdale:

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

Comme vous pouvez le constater, c’est un moyen relativement compact d’itérer sur des caractères en entrée standard. Vous pouvez utiliser d'autres expressions rationnelles pour changer la façon dont les choses correspondent.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.