Respostas:
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 length
quando obtém o comprimento de $_
.
Precisa iterar sobre os caracteres $_
? Substitua split//
por /./gs
. (Ou use /./g
se você também quiser pular novas linhas.) Isso funciona com outras variáveis: substitua split//,$x
por $x=~/./gs
.
Todo Perl embutido retorna algo. print
retorna 1, por exemplo, para indicar E / S bem-sucedida. Se você precisar inicializar $_
com um valor verdadeiro, por exemplo, $_=print$foo
permite 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 and
e 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 map
ou grep
. Mesmo palavras-chave como next
, last
e return
pode 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.
$foo
. Caso contrário, $_=1
é muito menor $_=print""
e tem o mesmo efeito.
$x
? Caso contrário, você poderia apenas fazer /./gs
e /./g
.
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 undef
padrã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 -i
opçã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.
-n
e colchetes incomparáveisÉ sabido que a opção de linha de comando -n
pode 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áveisDe maneira semelhante ao acima, o -p
comutador envolve while (<>) { ... ; print }
o programa.
Ao usar colchetes incomparáveis, perl -p 'code}{morecode'
será impresso apenas uma vez após a execução code
de 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.
#!perl -n
a primeira linha, certo?
}for(...){
em entre as chaves é muitas vezes bastante útil, bem como, por exemplo codegolf.stackexchange.com/a/25632
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 $n
para $_
, você pode mudar $n=<>;chop$n;print$n
para$_=<>;chop;print
Aqui, a print
função imprime o conteúdo de $_
por padrão e chop
também funciona $_
.
$_=<>;
obrigatório, não <>;
lê a linha $_
automaticamente?
$_=<>;print
e <>;print
. O primeiro repete para mim o que eu digito, enquanto o outro não.
print while(<>)
. Não tenho certeza se é um caso especial ou há alguma lógica coerente por trás dele, nem <>
da parte perlop
nem while
parte perlsyn
parecem mencionar esse comportamento.
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 ".
Use as variáveis especiais do Perl sempre que possível, por exemplo:
$"
vez de" "
$/
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
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 glob
sintaxe.
@i=<unique value>;
glob
A sintaxe também pode ser usada para efeitos interessantes.
@i=<item{1,2,3}>;
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 &&
.
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:
my
declare variáveis se puder evitá-lo. As únicas variáveis que realmente precisam my
sã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.print hello
não vai funcionar. Na verdade, significa print hello $_
(imprimir $_
em arquivo hello
).
print
, e agora eu não posso encontrar um bom e pequeno exemplo)
Tenho certeza de que alguns deles têm nomes formais e simplesmente não os conheço.
print $n++ while ($n < 10)
$var = join('',<>)
print ('X'*10) . "\n";
é maior queprint ('X'*10) . $/;
say
função do Perl é menor que print
, mas você precisará executar o código em -E
vez de-e
a..z
ou mesmo aa..zz
. Se necessário como uma string, use join
.$z = 'z'; print ++$z;
será exibidoaa
É tudo o que consigo pensar agora. Eu posso adicionar um pouco mais tarde.
print ('X'*10) . $/;
deveria fazer? Para mim, imprime 0
e 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 x
vez de *
ou algo assim?
while
, join'',<>;
também funciona sem eles.
Usando $%
vez de $a
pode permitir que você coloque o nome da variável para a direita ao lado de um if
, for
ou while
construir 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!
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 for
loops postfix dentro do arquivo map
.
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=$'
Se você ligar, digamos print
quatro 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
Se você tiver um código como:
$i--if$i>0
você pode usar:
$i-=$i>0
em vez disso, salve alguns bytes.
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
redo
adiciona comportamento de loop a um bloco sem for
ou while
. {redo}
é um loop infinito.
Não coloque parênteses em chamadas de função.
O Perl permite chamar uma função conhecida (principal ou predefinida) usando a NAME LIST
sintaxe. Isso permite que você solte o &
sigilo (se você ainda o estiver usando) e os parênteses.
Por exemplo: $v=join'',<>
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 $n
há 2 caracteres no Perl. Você pode alterar $n
para ()
sem nenhum custo e economizar 1 ponto e vírgula movendo a atribuição entre parênteses.
Você pode executar várias instruções diferentes dentro da lógica ternária aninhada.
Suponha que você tenha um grande if
- elsif
comunicado. 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)
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 $u
e nunca o definiu.
Se você vai usá-lo muito, a importação Time::HiRes
compensa, 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.
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.
Ocasionalmente (geralmente quando se lida com desafios de quine ou de fonte restrita ), 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.)
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.
$_=print""
é mais curto que$_=print$foo
.