Tipps zum Golfen in Perl?


47

Welche allgemeinen Tipps haben Sie zum Golfen in Perl? Ich bin auf der Suche nach Ideen, die sich auf Code-Golf-Probleme im Allgemeinen anwenden lassen, die zumindest etwas spezifisch für Perl sind (z. B. "Kommentare entfernen" ist keine Antwort). Bitte posten Sie einen Tipp pro Antwort.

Antworten:


26

TMTOWTDI

Das ist der wichtigste Perl-Golftipp, den Sie kennen müssen. Wenn Sie eine zu lange Folge von Zeichen betrachten, die Sie unbedingt benötigen, um Ihre Aufgabe zu erfüllen, fragen Sie sich, ob es keine andere Möglichkeit gibt, den gleichen Effekt mit einer anderen Funktion zu erzielen. Das gibt es normalerweise. Hier sind nur eine Handvoll:

  • ~~Erzwingt einen skalaren Kontext und ist 4 Zeichen kürzer als scalar.

  • y///cist ein char kürzer als lengthwenn man die länge von $_.

  • Müssen Sie die Zeichen in iterieren $_? Ersetzen split//durch /./gs. (Oder verwenden /./gSie, wenn Sie auch Zeilenumbrüche überspringen möchten.) Dies funktioniert mit anderen Variablen: Ersetzen split//,$xdurch $x=~/./gs.

  • Jedes eingebaute Perl gibt etwas zurück. printgibt beispielsweise 1 zurück, um eine erfolgreiche E / A anzuzeigen. Wenn Sie $_zum Beispiel auf einen wahren Wert initialisieren müssen , $_=print$fookönnen Sie zwei Fliegen mit einer Klappe schlagen.

  • Fast jede Anweisung in Perl kann als Ausdruck geschrieben werden, sodass sie in einer Vielzahl von Kontexten verwendet werden kann. Mehrere Anweisungen können zu mehreren Ausdrücken werden, die mit Kommas verkettet sind. Tests können mit Kurzschlussoperatoren ?: && ||sowie mit andund durchgeführt werden or, die dasselbe tun, jedoch mit einer niedrigeren Priorität als alle anderen Operatoren (einschließlich Zuweisung). Loops können über mapoder durchgeführt werden grep. Auch Schlüsselwörter wie next, lastund returnkann in einem Ausdruck Kontext verwendet werden, auch wenn sie nicht zurück! Wenn Sie diese Art von Transformationen berücksichtigen, haben Sie die Möglichkeit, Codeblöcke durch Ausdrücke zu ersetzen, die in eine Vielzahl von Kontexten eingefügt werden können.


$_=print""ist kürzer als $_=print$foo.
ASCIIThenANSI

5
Die Idee ist, dass Sie bereits drucken müssen $foo. Ansonsten $_=1ist viel kürzer als $_=print""und hat den gleichen Effekt.
Brotkasten

3
Meinen Sie zum dritten Mal, dass Sie über die Zeichen in iterieren $x? Ansonsten könnte man einfach /./gsund /./g.
Redbmk

25

Missbrauche Perls spezielle Variablen!

  • Wie in einer vorherigen Antwort erwähnt $/und $"standardmäßig mit "\n"und initialisiert " ".

  • $,und $\sind beide undefvoreingestellt und 3 Zeichen kürzer.

  • Wenn Sie $\einen Wert festlegen , wird dieser an jeden Wert angehängt print. Zum Beispiel: perl -ple '$\="=".hex.$/'ist ein praktischer Hex-zu-Dezimal-Konverter.

  • Wenn Sie keine Dateien über die Befehlszeile lesen, können Sie die -iBefehlszeilenoption als zusätzlichen Kanal für die Eingabe einer Zeichenfolge verwenden. Sein Wert wird in gespeichert $^I.

  • $=Erzwingt, dass alles, was ihm zugewiesen ist, eine Ganzzahl ist. Laufen Sie perl -ple '$_=$==$_'und geben Sie ihm verschiedene Einbrüche. Erzwingt ebenfalls, dass $-sein Wert eine nicht negative Ganzzahl ist (dh ein führender Strich wird als nicht numerisches Zeichen behandelt).

  • Sie können es $.als Boolesches Flag verwenden, das bei jeder Iteration einer while(<>)Schleife automatisch auf einen wahren Wert (ungleich Null) zurückgesetzt wird.


20

-n und unvergleichliche geschweifte Klammern

Es ist bekannt, dass der Befehlszeilenschalter -nverwendet werden kann, um das Skript einmal für jede Zeile auszuführen.

perl --help sagt:

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

Was nicht explizit gesagt wird, ist, dass Perl nicht nur eine Schleife um das Programm herum annimmt. es wickelt sich buchstäblich darum while (<>) { ... }.

Auf diese Weise sind die folgenden Befehle einander äquivalent:

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

-p und unvergleichliche geschweifte Klammern

Ähnlich wie oben wird das Programm mit dem -pSchalter umbrochen while (<>) { ... ; print }.

Wenn Sie nicht übereinstimmende geschweifte Klammern verwenden, perl -p 'code}{morecode'wird nach der Ausführung codefür alle Eingabezeilen nur einmal gedruckt , gefolgt von morecode.

Da $_undefiniert morecode;printist, wann ausgeführt wird, kann das Trennzeichen $\für Ausgabedatensätze zum Drucken der tatsächlichen Ausgabe missbraucht werden.

Zum Beispiel

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

Liest eine Zahl pro Zeile aus STDIN und gibt deren Summe aus.


Ich gehe davon aus, dass Sie dies #!perl -nin der ersten Zeile erreichen können, oder?
ASCIIThenANSI

@ASCIIThenANSI: Ja, das ist richtig. (Entschuldigung für die späte Antwort.)
Dennis

1
Ich glaube, ich habe die Kombination dieser drei Tricks (unvergleichliche geschweifte Klammern -pund $\​) zum ersten Mal in einer von @primos Perl-Antworten gesehen. Seine Antworten zu lesen ist ein guter Perl-Tipp für sich.
Dennis

1
Ein }for(...){zwischen die Klammern zu werfen ist oft auch ganz praktisch, zB codegolf.stackexchange.com/a/25632
primo

Eine Sache, die ich in Verbindung mit -n nützlich fand, ist der Operator || = Standardwertzuweisung. Kann vor der Schleife keinen Wert zuweisen.
Deltaray

18

Verwenden Sie $_diese Option , um skalare Referenzen zu entfernen. Es ist die spezielle Variable, die von den meisten Funktionen als Standard verwendet wird. Das Weglassen von Parametern ist eine Abkürzung, um auf diese Variable zu verweisen.

Durch die Änderung $nzu $_, können Sie ändern $n=<>;chop$n;print$nzu$_=<>;chop;print

Hier printdruckt die Funktion $_standardmäßig den Inhalt von und choparbeitet auch daran $_.


Ist $_=<>;erforderlich, <>;liest die Zeile nicht $_automatisch ein?
Sonntag,

Nein, das glaube ich nicht. Ich habe die Programme $_=<>;printund verglichen <>;print. Der erste wiederholt mir, was ich tippe, der andere nicht.
PhiNotPi

Oh, danke, es stellt sich heraus, dass das nur in Fällen wie passiert print while(<>). Ich bin mir nicht sicher, ob es sich um einen Sonderfall handelt oder ob eine zusammenhängende Logik dahinter steckt. Weder <>in perlopnoch in whileTeilen perlsynscheint dieses Verhalten zu erwähnen.
sundar

1
@sundar while(<>)ist ein Sonderfall, der in Perlsyn, I / O-Operatoren, dokumentiert ist: "Wenn und nur wenn das Eingabesymbol das einzige ist, was in der Bedingung einer" while " -Anweisung enthalten ist (auch wenn es als" for (;;) "getarnt ist. Schleife) wird der Wert automatisch der globalen Variablen $ _ zugewiesen, wodurch alles zerstört wird, was vorher da war. "
kernigh

17

Verwenden Sie die speziellen Variablen von Perl, wo immer Sie können, zB:

  • Verwenden Sie $"anstelle von" "
  • Verwenden Sie $/anstelle von"\n"

Sie haben den zusätzlichen Vorteil, dass sie mit Hilfe des Lexers eine garantierte Kennung mit einer Länge von einem Zeichen sind. Dies ermöglicht es, es mit dem darauf folgenden Schlüsselwort zu verkleben, wie in:print$.for@_

Die Liste aller speziellen Variablen finden Sie hier: Spezielle Variablen


15

Nicht verwenden qw. Dies ist Verschwendung von zwei Zeichen, die besser verwendet werden könnten. Schreiben Sie beispielsweise Folgendes nicht.

@i=qw(unique value);

Verwenden Sie stattdessen Barewords.

@i=(unique,value);

Wenn Sie keine Barewords verwenden können, verwenden Sie die globSyntax.

@i=<unique value>;

glob Die Syntax kann auch für interessante Effekte verwendet werden.

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

12

Verwenden Sie Anweisungsmodifizierer anstelle von zusammengesetzten Anweisungen.

Für zusammengesetzte Anweisungen sind in der Regel Klammern für das Argument und geschweifte Klammern für den Block erforderlich, während Anweisungsmodifikatoren keine benötigen.

Vergleichen Sie:

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

Beachten Sie, dass das letzte Beispiel zwar an die $c&&chop$,meisten Operationen mit mehreren Anweisungen anknüpft , aber für diese auch wirklich zu glänzen beginnt. Grundsätzlich alles, was den Vorrang des Bedieners verliert &&.


11

Nicht use strict. (Zitieren Sie mich nicht dazu, der PCG.SE-Kontext spielt eine Rolle.) Und, was noch wichtiger ist, codieren Sie nicht wie unter strikt. Die üblichen Verdächtigen:

  • myDeklarieren Sie keine Variablen, wenn Sie dies vermeiden können. Die einzigen Variablen, die wirklich benötigt werden, mysind diejenigen, die lexikalisch abgegrenzt werden sollen. Dies ist beim Golfen kaum einer der Fälle, in denen Sie keinen Oszilloskopschutz benötigen und die Rekursion in der Regel vollständig kontrollieren.
  • Zitieren Sie keine Strings mit einem Wort: ( Beispiel ). Stellen Sie jedoch sicher, dass Sie keine Funktion mit demselben Namen haben.

5
print hellowird nicht funktionieren. Es bedeutet eigentlich print hello $_(Drucken $_in Dateihandle hello).
Konrad Borowski

@GlitchMr danke! (und jetzt bin ich verblüfft, weil mein Punkt immer noch gültig ist, nur nicht mit print, und jetzt kann ich kein schönes und kurzes Beispiel finden)
JB

@JB hier ist ein gutes Beispiel: codegolf.stackexchange.com/a/8746/3348
Ardnew

11

Ich bin mir sicher, dass einige von ihnen formelle Namen haben und ich bin mir ihrer einfach nicht bewusst.

  • Wenn Sie eine while-Schleife haben (oder eine for-Schleife, die Sie in eine while-Schleife umwandeln können), können Sie das "while" nach dem Befehl definieren: print $n++ while ($n < 10)
  • Wenn Sie alles von STDIN in einen String lesen müssen: $var = join('',<>)
  • Wie CeilingSpy betonte, ist die Verwendung von $ / anstelle von \ n in bestimmten Situationen schneller: print ('X'*10) . "\n";länger alsprint ('X'*10) . $/;
  • Perls sayFunktion ist kürzer als print, aber Sie müssen den Code mit -Eanstelle von ausführen-e
  • Verwenden Sie Bereiche wie a..zoder sogar aa..zz. Bei Bedarf als Zeichenfolge verwenden join.
  • Inkrementieren von Zeichenfolgen: $z = 'z'; print ++$z;wird angezeigtaa

Das ist alles, woran ich gerade denken kann. Ich kann später noch etwas hinzufügen.


1
Was print ('X'*10) . $/;soll ich tun? Bei mir druckt es 0und kein Zeilenumbruch. Zum einen werden die Klammern zu einem Aufrufargument im Funktionsstil print, das enger als bindet .. Und meintest du xstattdessen *oder so?
Aschepler

Postfix benötigt keine Klammern whileund join'',<>;funktioniert auch ohne diese.
Choroba

10

Verwenden Sie Nicht-Wort-Zeichen als Variablennamen

Mit $%statt $aermöglicht es Ihnen , die Variablennamen zu setzen direkt neben ein if, foroder whilezu konstruieren , wie in:

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

Viele können verwendet werden, aber schauen Sie in den Dokumenten und in der Antwort von @ BreadBox nach, welche davon magische Effekte haben!


Verwenden Sie map, wenn Sie keine Anweisungsmodifikatoren verwenden können

Wenn Sie keine Anweisungsmodifikatoren gemäß der Antwort von @ JB verwenden können , speichert map möglicherweise ein Byte:

for(@c){} gegen map{}@c;

und ist nützlich, wenn Sie verschachtelte Iterationen ausführen möchten, da Sie Postfix- forSchleifen in das einfügen können map.


Verwenden Sie alle magischen Variablen für reguläre Ausdrücke

Perl verfügt über magische Variablen für 'Text vor dem Abgleich' und 'Text nach dem Abgleich', sodass es möglich ist, in Zweiergruppen mit möglicherweise weniger Zeichen aufzuteilen:

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

Dies kann auch als Ersatz für Folgendes verwendet werden substr:

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

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

Wenn Sie den Inhalt des Spiels benötigen, $&können verwendet werden, zB:

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

Ersetzen Sie Subs mit langen Namen durch einen kürzeren Namen

Wenn Sie printin Ihrem Code vier oder mehr Male sagen (dies hängt natürlich von der Länge der aufgerufenen Routine ab), ersetzen Sie ihn durch einen kürzeren Subnamen:

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

gegen

print;print;print;print

Ersetzen Sie bedingte Inkrementierer / Dekrementierer

Wenn Sie Code haben wie:

$i--if$i>0

Sie können verwenden:

$i-=$i>0

stattdessen, um einige Bytes zu speichern.


In Ganzzahl konvertieren

Wenn Sie keiner Variablen zuweisen und daher den Tipp der Brotbox nicht verwenden können, können Sie den folgenden Ausdruck verwenden 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

Beachten Sie jedoch, dass Sie keine Ganzzahl benötigen, um auf einen Array-Index zuzugreifen:

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

9

redoFügt einem Block ohne foroder ein Schleifenverhalten hinzu while. {redo}ist eine Endlosschleife.


7

Setzen Sie keine Funktionsaufrufe in Klammern.

Mit Perl können Sie eine bekannte (Core oder Predeclared) Funktion mithilfe der NAME LISTSyntax aufrufen . Auf diese Weise können Sie das &Siegel (falls Sie es noch verwendet haben) sowie die Klammern fallen lassen.

Zum Beispiel: $v=join'',<>

Vollständige Dokumentation


5

Versuchen Sie, den Wert eines Zuweisungsausdrucks wie folgt zu verwenden:

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

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

Das funktioniert, weil $nes in Perl 2 Zeichen gibt. Sie können kostenlos $nzu wechseln ()und 1 Semikolon speichern, indem Sie die Zuordnung in die Klammern setzen.


5

Sie können innerhalb der verschachtelten ternären Logik mehrere verschiedene Anweisungen ausführen.

Angenommen , Sie haben eine große if- elsifAnweisung. Dies kann eine beliebige Logik und eine beliebige Anzahl von Anweisungen sein.

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

Sie können (cmd1, cmd2, cmd3)innerhalb des ternären Operators alle Befehle ausführen.

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

Hier ist ein Dummy-Beispiel:

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

Verwenden Sie select(undef,undef,undef,$timeout)anstelle vonTime::HiRes

(Entnommen aus https://stackoverflow.com/a/896928/4739548 )

Bei vielen Herausforderungen müssen Sie präziser schlafen als ganze Zahlen. select()Das Timeout-Argument von kann genau das.

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

ist viel effizienter als:

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

Ersteres hat nur 20 Byte, während Letzteres 39 Byte belegt. Ersteres erfordert jedoch, dass Sie es nicht verwenden $uund es nie definiert haben.

Wenn Sie es viel verwenden werden, Time::HiReslohnt sich der Import , aber wenn Sie es nur einmal benötigen, select($u,$u,$u,0.1)spart die Verwendung von 19 Byte, was in den meisten Fällen definitiv eine Verbesserung darstellt.


1

Kürzen Sie Ihre Druckanweisungen

Sofern die Aufforderung nichts anderes angibt, brauchen Sie keine nachgestellten Zeilenumbrüche.
Unsere "Herausforderung" lautet "Ausgabe einer Zufallszahl von 0 bis 9 an STDOUT". Wir können diesen Code (28 Bytes) nehmen:

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

Und verkürzen Sie es auf dieses (25 Bytes):

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

indem Sie einfach die Variable drucken. Letzteres gilt nur für diese spezielle Herausforderung (19 Byte):

print int(rand(10))

Dies funktioniert jedoch nur, wenn Sie zwischen Zuweisung und Drucken nichts an der Variablen vornehmen müssen.


Die letzte ist bereits aufgeführt hier .
Sp3000,

@ Sp3000 Danke, ich habe meine Antwort aktualisiert.
ASCIIThenANSI,

1

Verwenden Sie Globs wie String-Literale

Gelegentlich (häufig bei oder Herausforderungen) profitieren Sie stark von der Fähigkeit, String-Literale zu verschachteln. Normalerweise würden Sie dies mit tun q(…). Abhängig davon, welche Zeichen Sie in der Zeichenfolge benötigen, können Sie möglicherweise ein Byte speichern und <…>den Glob-Operator verwenden. (Beachten Sie, dass das, was sich in den spitzen Klammern befindet, nicht wie ein Dateihandle aussehen kann und auch nicht so aussehen kann, als ob es zu einer Liste von Dateinamen erweitert werden soll, was bedeutet, dass eine ganze Reihe von Zeichen nicht richtig funktionieren.)


0

Verwenden Sie den Ausdruck Regex.

Ein gutes Beispiel dafür ist folgender Code, der die Eingabe in eine Sinuswelle umwandelt:

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

Wie Sie sehen, ist es eine recht kompakte Methode, Zeichen in der Standardeingabe zu durchlaufen. Sie können andere reguläre Ausdrücke verwenden, um die Art und Weise zu ändern, in der die Dinge aufeinander abgestimmt sind.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.