Consejos para jugar al golf en Perl?


47

¿Qué consejos generales tienes para jugar al golf en Perl? Estoy buscando ideas que puedan aplicarse a los problemas de golf de código en general que sean al menos algo específicos para Perl (por ejemplo, "eliminar comentarios" no es una respuesta). Por favor, publique un consejo por respuesta.

Respuestas:


26

TMTOWTDI

Ese es el consejo de golf Perl más importante que necesita saber. Siempre que esté viendo una secuencia de caracteres demasiado larga que tiene que tener para cumplir su tarea, pregúntese si no hay otra forma de obtener el mismo efecto con una función diferente. Por lo general hay. Aquí hay solo un puñado:

  • ~~aplica un contexto escalar y es 4 caracteres más corto que scalar.

  • y///ces un char más corto que lengthal obtener la longitud de $_.

  • ¿Necesita iterar sobre los caracteres en $_? Reemplazar split//con /./gs. (O /./gúselo si también desea omitir nuevas líneas). Esto funciona con otras variables: reemplazar split//,$xcon $x=~/./gs.

  • Cada Perl incorporado devuelve algo. printdevuelve 1, por ejemplo, para indicar E / S exitosa. Si necesita inicializar $_a un valor verdadero, por ejemplo, le $_=print$foopermite matar dos pájaros de un tiro.

  • Casi todas las declaraciones en Perl se pueden escribir como una expresión, lo que permite su uso en una variedad más amplia de contextos. Las declaraciones múltiples pueden convertirse en múltiples expresiones encadenadas junto con comas. Las pruebas se pueden realizar con operadores de cortocircuito ?: && ||, y también con andy or, que hacen lo mismo pero con una prioridad inferior a la de todos los demás operadores (incluida la asignación). Los bucles se pueden hacer a través de mapo grep. Incluso palabras claves como next, lasty returnpuede ser utilizado en un contexto de expresión, a pesar de que no vuelvan! Tener en cuenta este tipo de transformaciones le brinda la oportunidad de reemplazar los bloques de código con expresiones que pueden integrarse en una variedad más amplia de contextos.


$_=print""es más corto que $_=print$foo.
ASCIIThenANSI

55
La idea es que ya necesita imprimir $foo. De lo contrario, $_=1es mucho más corto $_=print""y tiene el mismo efecto.
breadbox

3
Para el tercero, ¿te refieres a iterar sobre los caracteres en $x? De lo contrario, podrías hacer /./gsy /./g.
redbmk

25

¡Abusa de las variables especiales de Perl!

  • Como se señaló en una respuesta anterior $/y $"se inicializan por defecto en "\n"y " ", respectivamente.

  • $,y $\están configurados undefpor defecto, y son 3 caracteres más cortos.

  • Establecer $\un valor hará que se agregue a todos print. Por ejemplo: perl -ple '$\="=".hex.$/'es un práctico convertidor hexadecimal a decimal.

  • Si no está leyendo archivos desde la línea de comandos, puede usar el -iinterruptor de la línea de comandos como un canal adicional para ingresar una cadena. Su valor se almacenará en $^I.

  • $=obliga a lo que se le asigna a ser un número entero. Intenta correr perl -ple '$_=$==$_'y darle varias entradas. Del mismo modo, $-obliga a su valor a ser un número entero no negativo (es decir, un guión inicial se trata como un carácter no numérico).

  • Puede usarlo $.como un indicador booleano que se restablece automáticamente a un valor verdadero (distinto de cero) en cada iteración de un while(<>)bucle.


20

-n y brackets rizados inigualables

Es bien sabido que el interruptor de línea de comando -nse puede utilizar para ejecutar el script una vez por cada línea.

perl --help dice:

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

Lo que no dice explícitamente es que Perl no solo asume un ciclo alrededor del programa; que , literalmente, se envuelve while (<>) { ... }alrededor de ella.

De esta manera, los siguientes comandos son equivalentes entre sí:

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

-p y brackets rizados inigualables

De manera similar a lo anterior, el -pinterruptor se envuelve while (<>) { ... ; print }alrededor del programa.

Al usar llaves sin igual, perl -p 'code}{morecode'solo se imprimirá una vez después de la ejecución codepara todas las líneas de entrada, seguido de morecode.

Como $_no se define cuando morecode;printse ejecuta, $\se puede abusar del separador de registros de salida para imprimir la salida real.

Por ejemplo

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

lee un número por línea de STDIN e imprime su suma.


Supongo que podrías lograr esto #!perl -nen la primera línea, ¿verdad?
ASCIIThenANSI

@ASCIIThenANSI: Sí, eso es correcto. (Perdón por la respuesta tardía.)
Dennis

1
Al dar crédito cuando se debe, creo que vi la combinación de estos tres trucos (llaves sin igual -py $\​) por primera vez en una de las respuestas de Perl de @primo . Leer sus respuestas es un buen consejo de Perl por sí solo.
Dennis

1
Lanzar un }for(...){paréntesis entre los aparatos también suele ser bastante útil, por ejemplo, codegolf.stackexchange.com/a/25632
primo el

Una cosa que encontré útil junto con -n es el operador de asignación de valor predeterminado || =. Evita no poder asignar un valor antes del ciclo.
deltaray

18

Se usa $_para eliminar referencias escalares. Es la variable especial que se usa por defecto en la mayoría de las funciones, y dejar de lado los parámetros es un atajo para hacer referencia a esta variable.

Al cambiar $na $_, puede cambiar $n=<>;chop$n;print$na$_=<>;chop;print

Aquí, la printfunción imprime el contenido de $_forma predeterminada y choptambién funciona $_.


Es $_=<>;obligatorio, ¿no <>;lee la línea $_automáticamente?
Sundar - Restablecer Monica

No, no lo creo. Comparé los programas $_=<>;printy <>;print. El primero me repite lo que escribo, mientras que el otro no.
PhiNotPi

Oh, gracias, resulta que solo sucede en casos como print while(<>). No estoy seguro si se trata de un caso especial o si hay una lógica coherente detrás de él, ni <>parte perlopni whileparte de perlsynparece mencionar este comportamiento.
Sundar - Restablecer Monica

1
@sundar while(<>)es un caso especial, documentado en perlsyn, Operadores de E / S: 'Si y solo si el símbolo de entrada es lo único dentro del condicional de una declaración "while" (incluso si se disfraza como "for (;;)" loop), el valor se asigna automáticamente a la variable global $ _, destruyendo lo que estaba allí anteriormente. "
kernigh

17

Utilice las variables especiales de Perl siempre que pueda, por ejemplo:

  • Usar en $"lugar de" "
  • Usar en $/lugar de"\n"

Tienen el beneficio adicional de ser un identificador largo garantizado de un carácter, con la ayuda del lexer. Esto permite pegarlo a la palabra clave que lo sigue, como en:print$.for@_

La lista de todas las variables especiales está disponible aquí: Variables especiales


15

No utilice qw. Esto es un desperdicio de dos personajes que podrían usarse de una mejor manera. Por ejemplo, no escriba lo siguiente.

@i=qw(unique value);

En su lugar, use palabras vacías.

@i=(unique,value);

O si no puede usar palabras simples, use la globsintaxis.

@i=<unique value>;

glob La sintaxis también se puede utilizar para efectos interesantes.

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

12

Utilice modificadores de declaración en lugar de declaraciones compuestas.

Las declaraciones compuestas tienden a requerir paréntesis para el argumento y llaves para el bloque, mientras que los modificadores de declaraciones no necesitan ninguna.

Comparar:

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

Tenga en cuenta que el último ejemplo se vincula con $c&&chop$,, pero comienza a brillar realmente para la mayoría de las operaciones de múltiples declaraciones. Básicamente todo lo que pierde la prioridad del operador &&.


11

No hacerlo use strict. (no me cite sobre esto, el contexto PCG.SE es importante) Y, lo que es más importante, no codifique como si fuera estricto. Los sospechosos de siempre:

  • no mydeclare variables si puede evitarlo. Las únicas variables que realmente necesitan myson aquellas que desea tener un alcance léxico. Eso es apenas uno de ellos cuando se juega al golf, donde no necesita protección de alcance y tiende a controlar completamente la recursividad.
  • no cite cadenas de una palabra: ( ejemplo ). Sin embargo, asegúrese de no tener una función con el mismo nombre.

55
print hellono funciona En realidad significa print hello $_(imprimir $_en el controlador de archivo hello).
Konrad Borowski

@ GlitchMr gracias! (y ahora estoy desanimado, porque mi punto aún es válido, pero no con print, y ahora no puedo encontrar un buen y breve ejemplo)
JB

@JB aquí hay un buen ejemplo: codegolf.stackexchange.com/a/8746/3348
ardnew

11

Estoy seguro de que algunos de estos tienen nombres formales y simplemente no estoy al tanto de ellos.

  • Si tiene un ciclo while (o un ciclo for que puede convertir en un ciclo while), puede definir el "while" después del comando: print $n++ while ($n < 10)
  • Si necesita leer todo, desde STDIN en una cadena: $var = join('',<>)
  • Como señaló CeilingSpy, usar $ / en lugar de \ n es más rápido en algunas situaciones: print ('X'*10) . "\n";es más largo queprint ('X'*10) . $/;
  • La sayfunción de Perl es más corta que print, pero tendrá que ejecutar el código en -Elugar de-e
  • Use rangos como a..zo incluso aa..zz. Si es necesario como una cadena, use join.
  • Incremento de cadenas: $z = 'z'; print ++$z;se mostraráaa

Eso es todo lo que puedo pensar en este momento. Puedo agregar algo más más tarde.


1
¿Qué se print ('X'*10) . $/;supone que debe hacer? Para mí se imprime 0y no hay línea nueva. Por un lado, los paréntesis se convierten en un argumento de llamada de estilo de función print, que se une más fuerte que .. ¿Y quiso decir en xlugar de *o algo así?
aschepler

No se necesitan paréntesis en postfix while, join'',<>;también funciona sin ellos.
choroba

10

Use caracteres que no sean palabras como nombres de variables

Utilizando $%en lugar de $ase permitirá colocar el nombre de la variable justo al lado de una if, foro whileconstruir como en:

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

Se pueden usar muchos, pero revisa los documentos y la respuesta de @ BreadBox para saber cuáles tienen efectos mágicos.


Use el mapa cuando no pueda usar modificadores de instrucciones

Si no puede usar modfiers de declaración según la respuesta de @ JB , el mapa puede guardar un byte:

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

y es útil si desea hacer iteraciones anidadas, ya que puede colocar forbucles postfix dentro de map.


Usa todas las variables mágicas de Expresión Regular

Perl tiene variables mágicas para 'texto antes del partido' y 'texto después del partido', por lo que es posible dividirlo en grupos de dos con potencialmente menos caracteres:

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

Esto también podría funcionar bien como un reemplazo para substr:

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

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

Si necesita el contenido del partido, $&se puede utilizar, por ejemplo:

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

Reemplazar subs por nombres largos con un nombre más corto

Si llama por ejemplo printcuatro o más veces en su código (esto obviamente varía con la duración de la rutina que está llamando), reemplácelo con un subnombre más corto:

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

vs.

print;print;print;print

Reemplazar incrementos / decrementadores condicionales

Si tienes un código como:

$i--if$i>0

puedes usar:

$i-=$i>0

en cambio para guardar algunos bytes.


Convertir a entero

Si no está asignando a una variable y no puede usar la sugerencia de breadbox , puede usar la expresión 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

Sin embargo, vale la pena señalar que no necesita usar un número entero para acceder a un índice de matriz:

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

9

redoagrega comportamiento de bucle a un bloque sin foro while. {redo}Es un bucle infinito.


7

No haga paréntesis de llamadas a funciones.

Perl le permite llamar a una función conocida (núcleo o predeclarada) utilizando la NAME LISTsintaxis. Esto le permite soltar el &sigilo (si todavía lo estaba usando), así como los paréntesis.

Por ejemplo: $v=join'',<>

Documentación completa


5

Intente usar el valor de una expresión de asignación, así:

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

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

Esto funciona porque $nhay 2 caracteres en Perl. Puede cambiar $na ()sin costo y guardar 1 punto y coma moviendo la asignación entre paréntesis.


5

Puede ejecutar varias declaraciones diferentes dentro de la lógica ternaria anidada.

Suponga que tiene un gran if- elsifcomunicado. Esto podría ser cualquier lógica y cualquier número de declaraciones.

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

Puede usar (cmd1, cmd2, cmd3)dentro del operador ternario para ejecutar todos los comandos.

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

Aquí hay un ejemplo ficticio:

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

Usar en select(undef,undef,undef,$timeout)lugar deTime::HiRes

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

Muchos desafíos requieren que duermas con mayor precisión que los enteros. select()El argumento del tiempo de espera puede hacer exactamente eso.

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

es mucho más eficiente que:

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

El primero tiene solo 20 bytes, mientras que el último ocupa 39. Sin embargo, el primero requiere que no lo esté utilizando $uy nunca lo haya definido.

Si lo va a usar mucho, la importación Time::HiResvale la pena, pero si solo lo necesita una vez, el uso select($u,$u,$u,0.1)ahorra 19 bytes, lo que definitivamente es una mejora en la mayoría de los casos.


1

Acorte sus declaraciones impresas

A menos que el desafío especifique lo contrario, no necesita líneas nuevas.
Nuestro 'desafío' dice 'generar un número aleatorio de 0 a 9 a STDOUT'. Podemos tomar este código (28 bytes):

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

Y acortarlo a esto (25 bytes):

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

simplemente imprimiendo la variable. Este último solo se aplica específicamente a este desafío (19 bytes):

print int(rand(10))

pero eso solo funciona cuando no tiene que hacer nada a la variable entre la asignación y la impresión.


El último ya figura aquí .
Sp3000

@ Sp3000 Gracias, he actualizado mi respuesta.
ASCIIThenANSI

1

Use globos como literales de cadena

Ocasionalmente (a menudo cuando se trata de desafíos de o ) se beneficia enormemente de la capacidad de anidar literales de cadena. Normalmente, harías esto con q(…). Sin embargo, dependiendo de los caracteres que necesite dentro de la cadena, puede guardar un byte y usar <…>el operador global. (Tenga en cuenta que lo que está dentro de los corchetes angulares no puede verse como un identificador de archivo, y no puede parecer que está destinado a expandirse en una lista de nombres de archivo, lo que significa que muchos caracteres no funcionarán correctamente).


0

Usa la expresión regexe.

Una buena ilustración de esto es el siguiente código que da forma a la entrada en onda sinusoidal:

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

Como puede ver, es una forma bastante compacta de iterar sobre los caracteres en la entrada estándar. Podría usar otra expresión regular para cambiar la forma en que se combinan las cosas.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.