Dicas para jogar golfe em Perl?


47

Que dicas gerais você tem para jogar golfe no Perl? Estou procurando idéias que possam ser aplicadas aos problemas de código de golfe em geral que sejam pelo menos um pouco específicos para Perl (por exemplo, "remover comentários" não é uma resposta). Poste uma dica por resposta.

Respostas:


26

TMTOWTDI

Essa é a dica de golfe Perl mais importante que você precisa conhecer. Sempre que você estiver olhando para uma sequência muito longa de caracteres que você absolutamente precisa ter para realizar sua tarefa, pergunte a si mesmo se não há outra maneira de obter o mesmo efeito usando um recurso diferente. Geralmente existe. Aqui estão apenas algumas:

  • ~~impõe um contexto escalar e é 4 caracteres menor que scalar.

  • y///cé um caractere mais curto do que lengthquando obtém o comprimento de $_.

  • Precisa iterar sobre os caracteres $_? Substitua split//por /./gs. (Ou use /./gse você também quiser pular novas linhas.) Isso funciona com outras variáveis: substitua split//,$xpor $x=~/./gs.

  • Todo Perl embutido retorna algo. printretorna 1, por exemplo, para indicar E / S bem-sucedida. Se você precisar inicializar $_com um valor verdadeiro, por exemplo, $_=print$foopermite matar dois coelhos com uma cajadada só.

  • Quase todas as declarações no Perl podem ser escritas como uma expressão, permitindo que sejam usadas em uma variedade maior de contextos. Várias instruções podem se tornar várias expressões encadeadas por vírgulas. Os testes podem ser feitos com operadores de curto-circuito ?: && ||e também com ande or, que fazem a mesma coisa, mas com precedência menor do que todos os outros operadores (incluindo a atribuição). Os loops podem ser feitos via mapou grep. Mesmo palavras-chave como next, laste returnpode ser usado em um contexto expressão, mesmo que eles não voltar! Tendo em mente esses tipos de transformações, você tem a oportunidade de substituir blocos de código por expressões que podem ser inseridas em uma variedade maior de contextos.


$_=print""é mais curto que $_=print$foo.
ASCIIThenANSI

5
A ideia é que você já precise imprimir $foo. Caso contrário, $_=1é muito menor $_=print""e tem o mesmo efeito.
breadbox

3
Para o terceiro, você quer dizer iterando sobre os caracteres $x? Caso contrário, você poderia apenas fazer /./gse /./g.
redbmk

25

Abuse as variáveis ​​especiais do Perl!

  • Conforme observado em uma resposta anterior, $/e $"são inicializados por padrão para "\n"e " ", respectivamente.

  • $,e $\são definidos como undefpadrão e são 3 caracteres mais curtos.

  • Definir $\um valor fará com que ele seja anexado a todos print. Por exemplo: perl -ple '$\="=".hex.$/'é um prático conversor hexadecimal para decimal.

  • Se você não estiver lendo arquivos da linha de comando, poderá usar a -iopção de linha de comando como um canal extra para inserir uma string. Seu valor será armazenado em $^I.

  • $=força o que for atribuído a ele como um número inteiro. Tente executar perl -ple '$_=$==$_'e dar várias entradas. Da mesma forma, $-força seu valor a ser um número inteiro não negativo (ou seja, um traço à esquerda é tratado como um caractere não numérico).

  • Você pode usar $.como um sinalizador booleano que é redefinido automaticamente para um valor verdadeiro (diferente de zero) em todas as iterações de um while(<>)loop.


20

-n e colchetes incomparáveis

É sabido que a opção de linha de comando -npode ser usada para executar o script uma vez para cada linha.

perl --help diz:

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

O que não diz explicitamente é que o Perl não assume apenas um loop ao redor do programa; ele literalmente envolve while (<>) { ... }em torno dele.

Dessa forma, os seguintes comandos são equivalentes entre si:

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

-p e colchetes incomparáveis

De maneira semelhante ao acima, o -pcomutador envolve while (<>) { ... ; print }o programa.

Ao usar colchetes incomparáveis, perl -p 'code}{morecode'será impresso apenas uma vez após a execução codede todas as linhas de entrada, seguidas por morecode.

Como não $_é definido quando morecode;printé executado, o separador de registros de saída $\pode ser usado com abuso para imprimir a saída real.

Por exemplo

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

lê um número por linha de STDIN e imprime sua soma.


Estou assumindo que você poderia fazer isso com #!perl -na primeira linha, certo?
ASCIIThenANSI

@ASCIIThenANSI: Sim, isso está correto. (Desculpe pela resposta tardia.)
Dennis

1
Dando crédito onde o crédito é devido, acho que vi a combinação desses três truques (colchetes incomparáveis -pe $\​) pela primeira vez em uma das respostas Perl do @primo . Ler suas respostas é uma boa dica do Perl por si só.
Dennis

1
Jogando um }for(...){em entre as chaves é muitas vezes bastante útil, bem como, por exemplo codegolf.stackexchange.com/a/25632
primo

Uma coisa que achei útil em conjunto com -n é o operador de atribuição de valor padrão || =. Evita que seja possível atribuir um valor antes do loop.
Deltaray

18

Use $_para eliminar referências escalares. É a variável especial que é usada como padrão pela maioria das funções, e deixar de fora os parâmetros é um atalho para fazer referência a essa variável.

Ao mudar $npara $_, você pode mudar $n=<>;chop$n;print$npara$_=<>;chop;print

Aqui, a printfunção imprime o conteúdo de $_por padrão e choptambém funciona $_.


É $_=<>;obrigatório, não <>;lê a linha $_automaticamente?
Sundar

Não, eu não penso assim. Eu comparei os programas $_=<>;printe <>;print. O primeiro repete para mim o que eu digito, enquanto o outro não.
PhiNotPi

Oh, obrigado, acontece que acontece apenas em casos como print while(<>). Não tenho certeza se é um caso especial ou há alguma lógica coerente por trás dele, nem <>da parte perlopnem whileparte perlsynparecem mencionar esse comportamento.
sundar

1
@sundar while(<>)é um caso especial, documentado em perlsyn, operadores de E / S: 'Se e somente se o símbolo de entrada for a única coisa dentro da condição de uma declaração "while" (mesmo se disfarçada de "for (;;)" circular), o valor é automaticamente atribuído à variável global $ _, destruindo tudo o que estava lá anteriormente ".
kernigh

17

Use as variáveis ​​especiais do Perl sempre que possível, por exemplo:

  • Use em $"vez de" "
  • Use em $/vez de"\n"

Eles têm o benefício adicional de serem um identificador de um caractere garantido, com a ajuda do lexer. Isso possibilita colá-lo à palavra-chave a seguir, como em:print$.for@_

A lista de todas as variáveis ​​especiais está disponível aqui: Variáveis ​​Especiais


15

Não use qw. Isso é desperdício de dois caracteres que poderiam ser usados ​​da melhor maneira. Por exemplo, não escreva o seguinte.

@i=qw(unique value);

Em vez disso, use palavras de barras.

@i=(unique,value);

Ou, se você não pode usar palavras de barra, use a globsintaxe.

@i=<unique value>;

glob A sintaxe também pode ser usada para efeitos interessantes.

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

12

Use modificadores de instrução em vez de instruções compostas.

Instruções compostas tendem a exigir parênteses para o argumento e chaves para o bloco, enquanto modificadores de instruções não precisam de nenhuma.

Comparar:

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

Observe que o último exemplo está vinculado $c&&chop$,, mas começa realmente a brilhar na maioria das operações com várias instruções. Basicamente, qualquer coisa que perca precedência do operador &&.


11

Não use strict. (não me cite, o contexto PCG.SE é meio importante) E, mais importante, não codifique como se estivesse sob rigoroso. Os suspeitos do costume:

  • não mydeclare variáveis ​​se puder evitá-lo. As únicas variáveis ​​que realmente precisam mysão aquelas que você deseja definir com escopo lexical. Isso quase não ocorre no golfe, onde você não precisa de proteção de escopo e tende a controlar totalmente a recursão.
  • não cite cadeias de caracteres de uma palavra: ( exemplo ). Porém, verifique se você não possui uma função com o mesmo nome.

5
print hellonão vai funcionar. Na verdade, significa print hello $_(imprimir $_em arquivo hello).
22412 Konrad Borowski

@GlitchMr thanks! (e agora estou chateado, porque o meu ponto ainda é válido, não apenas com print, e agora eu não posso encontrar um bom e pequeno exemplo)
JB

@JB, aqui está um bom exemplo: codegolf.stackexchange.com/a/8746/3348 #
arnew

11

Tenho certeza de que alguns deles têm nomes formais e simplesmente não os conheço.

  • Se você tiver um loop while (ou um loop for, você pode transformá-lo em um loop while), poderá definir o "while" após o comando: print $n++ while ($n < 10)
  • Se você precisar ler tudo, desde STDIN em uma string: $var = join('',<>)
  • Como o CeilingSpy apontou, usar $ / em vez de \ n é mais rápido em algumas situações: print ('X'*10) . "\n";é maior queprint ('X'*10) . $/;
  • A sayfunção do Perl é menor que print, mas você precisará executar o código em -Evez de-e
  • Use intervalos como a..zou mesmo aa..zz. Se necessário como uma string, use join.
  • Incrementando strings: $z = 'z'; print ++$z;será exibidoaa

É tudo o que consigo pensar agora. Eu posso adicionar um pouco mais tarde.


1
O que print ('X'*10) . $/;deveria fazer? Para mim, imprime 0e não há nova linha. Por um lado, os parênteses se tornam um argumento de chamada no estilo de função print, que se liga mais estreitamente que .. E você quis dizer em xvez de *ou algo assim?
Aschepler

Sem parênteses necessários no postfix while, join'',<>;também funciona sem eles.
choroba

10

Use caracteres que não sejam palavras como nomes de variáveis

Usando $%vez de $apode permitir que você coloque o nome da variável para a direita ao lado de um if, forou whileconstruir como em:

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

Muitos podem ser usados, mas verifique os documentos e a resposta do @ BreadBox para quais têm efeitos mágicos!


Use map quando não puder usar modificadores de instrução

Se você não pode usar modificadores de declaração de acordo com a resposta de @ JB , o mapa pode salvar um byte:

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

e é útil se você deseja fazer iterações aninhadas, pois é possível colocar forloops postfix dentro do arquivo map.


Use todas as variáveis ​​mágicas de expressão regular

O Perl possui variáveis ​​mágicas para 'texto antes da correspondência' e 'texto após correspondência', portanto, é possível dividir em grupos de dois com 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!

Isso também pode funcionar bem como um substituto para substr:

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

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

Se você precisar do conteúdo da partida, $&pode ser usado, por exemplo:

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

Substituir subs por nomes longos por um nome mais curto

Se você ligar, digamos printquatro ou mais vezes em seu código (isso obviamente varia com a duração da rotina que você está chamando), substitua-o por um subnome mais curto:

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

vs.

print;print;print;print

Substituir incrementadores / decrementadores condicionais

Se você tiver um código como:

$i--if$i>0

você pode usar:

$i-=$i>0

em vez disso, salve alguns bytes.


Converter em número inteiro

Se você não está atribuindo a uma variável e não pode usar a dica da caixa de pão , pode usar a expressão 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

No entanto, vale a pena notar que você não precisa usar um número inteiro para acessar um índice de matriz:

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

9

redoadiciona comportamento de loop a um bloco sem forou while. {redo}é um loop infinito.


7

Não coloque parênteses em chamadas de função.

O Perl permite chamar uma função conhecida (principal ou predefinida) usando a NAME LISTsintaxe. Isso permite que você solte o &sigilo (se você ainda o estiver usando) e os parênteses.

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

Documentação completa


5

Tente usar o valor de uma expressão de atribuição, da seguinte maneira:

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

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

Isso funciona porque $nhá 2 caracteres no Perl. Você pode alterar $npara ()sem nenhum custo e economizar 1 ponto e vírgula movendo a atribuição entre parênteses.


5

Você pode executar várias instruções diferentes dentro da lógica ternária aninhada.

Suponha que você tenha um grande if- elsifcomunicado. Pode ser qualquer lógica e qualquer número de instruções.

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

Você pode usar (cmd1, cmd2, cmd3)dentro do operador ternário para executar todos os 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

Aqui está um exemplo fictício:

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

Use em select(undef,undef,undef,$timeout)vez deTime::HiRes

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

Muitos desafios exigem que você durma com maior precisão do que números inteiros. select()O argumento de tempo limite pode fazer exatamente isso.

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

é muito mais eficiente que:

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

O primeiro tem apenas 20 bytes, enquanto o último ocupa 39. No entanto, o primeiro exige que você não esteja usando $ue nunca o definiu.

Se você vai usá-lo muito, a importação Time::HiRescompensa, mas se você precisar apenas uma vez, o uso select($u,$u,$u,0.1)salva 19 bytes, o que é definitivamente uma melhoria na maioria dos casos.


1

Encurte suas declarações de impressão

A menos que o desafio especifique o contrário, não é necessário rastrear novas linhas.
Nosso 'desafio' diz 'gera um número aleatório de 0 a 9 para STDOUT'. Podemos pegar este código (28 bytes):

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

E reduza para isso (25 bytes):

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

simplesmente imprimindo a variável. Este último se aplica apenas a esse desafio especificamente (19 bytes):

print int(rand(10))

mas isso só funciona quando você não precisa fazer nada com a variável entre atribuição e impressão.


O último já está listado aqui .
Sp3000

@ Sp3000 Obrigado, atualizei minha resposta.
ASCIIThenANSI

1

Use globs como literais de string

Ocasionalmente (geralmente quando se lida com desafios de ou ), você se beneficia muito da capacidade de aninhar literais de strings. Normalmente, você faria isso com q(…). No entanto, dependendo de quais caracteres você precisa dentro da string, você poderá salvar um byte e usar <…>o operador glob. (Observe que o que está dentro dos colchetes angulares não pode parecer um identificador de arquivo e não pode ser expandido para uma lista de nomes de arquivos, o que significa que muitos caracteres não funcionarão corretamente.)


0

Use a expressão regexe.

Uma ilustração decente disso é o seguinte código que forma a entrada em onda senoidal:

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

Como você pode ver, é uma maneira bastante compacta de iterar caracteres na entrada padrão. Você pode usar outra regex para alterar a maneira como as coisas são correspondidas.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.