Por que Perl avalia o código em ${…} durante a interpolação de strings?
-
22-09-2019 - |
Pergunta
Por que o trecho a seguir funciona?E que mal poderia ser possível usando isso?Mas, falando sério, há alguma razão para o código em ${}
é avaliado e depois usado como referência escalar?
use strict;
no strict 'refs';
our $message = "Hello world!";
print "${ lc 'MESSAGE' }\n";
Solução
Tudo bem, A menos que você use referências simbólicas. Suponha que o seguinte código:
my %messages = (hello => "Hello world!", bye => "Bye-bye, world!");
sub get_message_ref { return \$messages{$_[0]} }; # returns scalarref
print "${ get_message_ref('bye') }\n";
Concordo, sua utilidade não é óbvia nos escalares, mas é muito útil com o ArrayRefs.
print "keys: @{[keys %messages]}\n";
Outras dicas
Explicamos isso em profundidade em Perl intermediário.
A sintaxe geral para pesquisas de variáveis é:
SIGIL BLOCK INDEXY-THING
Para um escalar simples parecido com:
print $ { foo };
Você provavelmente já viu isso quando precisa separar o nome de uma variável das coisas que a cercam:
print "abc${foo}def\n";
Se você tiver apenas um identificador Perl no bloco e nenhuma bagunça ao redor, poderá deixar de lado os colchetes, que é o caso comum:
print $foo;
No entanto, é a mesma coisa para desreferenciar uma referência:
SIGIL BLOCK-RETURNING-REFERENCE INDEXY-THINGS
Se o que você obtém no bloco é uma referência, Perl tenta desreferencia-lo como você pediu também:
my $ref = \ '12345';
print $ { $ref };
Mas isso é um verdadeiro bloqueio, e não apenas açúcar.Você pode ter quantas declarações quiser lá:
print $ { my $ref = \ '1234'; $ref };
Agora você não está apenas especificando um identificador Perl, então o Perl não assume que você está fornecendo um identificador e executa o código e usa o resultado como referência.Considere a diferença entre estes quase idênticos say
declarações:
use 5.010;
our $foo = "I'm the scalar";
sub foo { \ "I'm the sub" }
say ${foo};
say ${foo;};
Naquele segundo say
Perl vê o ponto e vírgula, percebe que não é um identificador, interpreta o código entre colchetes como texto e retorna o resultado.Como o resultado é uma referência, ele usa o ${...}
para desreferencia-lo.Não importa onde você faz isso, então fazê-lo dentro de uma string entre aspas duplas não é especial.
Além disso, observe o our
lá.Isso é importante agora que você considerará algo um pouco mais complicado:
use 5.010;
our $foo = "I'm the scalar";
sub foo { \ "I'm the sub" }
sub baz { 'foo' }
say ${foo};
say ${foo;};
say ${baz;};
Perl interpreta o que dura say
como código e vê o resultado não é uma referência;é a string simples foo
.Perl vê que não é uma referência, mas agora está em um contexto de desreferenciação, então faz uma referência simbólica (como Greg Bacon descreve).Como as referências simbólicas funcionam com variáveis na tabela de símbolos, isso $foo
tinha que ser uma variável de pacote.
Já que é fácil bagunçar tudo, strict
tem uma verificação útil para isso.Porém, ao desligá-lo, não se surpreenda quando ele te morder.:)
De Seção "Usando referências" da documentação de Perlref:
Em qualquer lugar que você colocasse um identificador (ou cadeia de identificadores) como parte de uma variável ou nome da sub -rotina, você pode substituir o identificador por um bloco retornando uma referência do tipo correto. Em outras palavras, os exemplos anteriores podem ser escritos assim:
$bar = ${$scalarref}; push(@{$arrayref}, $filename); ${$arrayref}[0] = "January"; ${$hashref}{"KEY"} = "VALUE"; &{$coderef}(1,2,3); $globref->print("output\n"); # iff IO::Handle is loaded
É certo que é um pouco bobo usar os enrolados neste caso, mas o bloco pode conter qualquer expressão arbitrária, em particular, expressões subcritivas:
&{ $dispatch{$index} }(1,2,3); # call correct routine
Por ser capaz de omitir os enrolados pelo caso simples de
$$x
, as pessoas geralmente cometem o erro de ver os símbolos de desreferência como operadores adequados e se perguntam sobre sua precedência. Se fossem, porém, você poderia usar parênteses em vez de aparelhos. Esse não é o caso. Considere a diferença abaixo; O caso 0 é uma versão curta do caso 1, não o caso 2:$$hashref{"KEY"} = "VALUE"; # CASE 0 ${$hashref}{"KEY"} = "VALUE"; # CASE 1 ${$hashref{"KEY"}} = "VALUE"; # CASE 2 ${$hashref->{"KEY"}} = "VALUE"; # CASE 3
O caso 2 também é enganoso, pois você está acessando uma variável chamada
%hashref
, não desreferenciando$hashref
Para o hash, presumivelmente está referenciando. Esse seria o caso 3.
Mais tarde em "Referências simbólicas":
Dissemos que as referências surgem em existência, conforme necessário, se estiverem indefinidas, mas não dissemos o que acontece se um valor usado como referência já estiver definido, mas não é uma referência difícil. Se você o usar como referência, ele será tratado como uma referência simbólica. Ou seja, o valor do escalar é considerado o nome de uma variável, em vez de um link direto para um valor (possivelmente) anônimo.