Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Resolvi escrever este post porque:
1. Percebi que esta seção não está muito ativa ultimamente;
2. A não ser que eu esteja muito enganado, a quantidade de material sobre esse assunto em português é escarssa.
E principalmente...
3. Amanhã não vou trabalhar.
Pretendo falar aqui, brevemente, sobre operadores lógicos e "lazy evaluation". Acredito que muito será esclarescido pra quem nunca leu um livro bom sobre Perl de cabo a rabo.
Sem mais blá-blá-blá...
-----
Em Perl, existem operadores unários, binários e um operador ternário ("?:", derivado do C). A classificação se refere a quantos objetos são manipulados pelo operador. Vamos falar dos binários. Duas operações muito comuns em programação são o AND e o OR lógicos. Se você já programou em Perl, deve saber que, para estas duas operações, há quatro operadores. De fato:
and e &∨ e ||
"and" e "&&" são equivalentes, certo? Errado. Há um motivo (muito importante) pelo qual os desenvolvedores da linguagem implementaram mais de um operador para realizar a mesma operação. A diferença, que a princípio parece obscura, é fundamental: a precedência.
O que é precedência?
Nada mais que uma convenção sobre a ordem de execução de operações em uma expressão. Complicado? Não deveria ser. Se você sabe aplicar as 4 operações fundamentais da matemática, responda:
1. Qual valor a variável possui após a computação da seguinte expressão?
my $valor = 1 + 2 + 3 * 3;
Todos sabemos que é 12. A multiplicação é executada primeiro, depois as somas. Isso porque, nas regras da matemática (e o Perl, assim como todas as outras linguagens de programação, deve transcrever fielmente essas regras), a multiplicação e a divisão têm precedência mais alta que a adição e subtração. A tabela de precedências das operações básicas é a seguinte (precedências mais altas ficam acima de precedências mais baixas):
multiplicação, divisão // computar da esquerda para a direita adição, subtração // computar da esquerda para a direita
Com isso, não importa a complexidade aparente da expressão. Seguindo as regras de precedência, sabemos que
print 5 4 / 10 + 6 2 / 3 - 13 + 50 + 2 * 8;
Imprime "59".
De volta aos operadores lógicos. Qual valor será impresso ao executarmos o código a seguir?
Se você acha que o programa acima imprime "1", então precisa se lembrar do que eu disse acima: a precedência de "or" é baixíssima. Talvez não seja justo cobrar a resposta certa ainda, pois não forneci a tabela de precedências. Então aí vai, direto do perlop:
left terms and list operators (leftward) left -> nonassoc ++ -- right ** right ! ~ and unary + and - left =~ !~ left / % x left + - . left << >> nonassoc named unary operators nonassoc < > <= >= lt gt le ge nonassoc == != <=> eq ne cmp left & left | ^ left && left || nonassoc .. ... right ?: right = += -= = etc. left , => nonassoc list operators (rightward) right not left and left or xor
Agora sim! Vemos, então que a precedência da associação, "=", é mais alta que a do "or". Por isso, "traduzindo" nosso programa, ficamos com:
my ($x, $y, $resultado);$y = 1;($resultado = $x) OU $y;IMPRIMIR $resultado;
Portanto, acabamos de imprimir undef (que, obviamente, não imprime nada). É por isso que às vezes pessoas acham estranho que
my ($x, $y, $resultado);$x = 0$y = 1;$resultado = $x or $y;print $resultado;
imprima "0" na tela (afinal de contas, um OR entre 0 e 1 deveria retornar 0). O caso é que o "or", aí, não opera sobre $x e $y, mas sobre ($resultado = $x) e $y.
Para consertar nosso programa, usamos o operador de precedência mais alta:
Quando, então, seria vantagem usar o operador de baixa precedência? É aqui que introduzimos...
Lazy Evaluation
Lazy evaluation é um princípio seguido pela engine do Perl, de acordo com o qual ele calcula o resultado de uma expressão analisando a menor porção possível dela. Exemplificando, se tivermos:
my $var = 1 || 0 || 0 || 0 || 0 || 0 || 0;
O Perl vai armazenar em $var o resultado da expressão "1 || 0 || 0 || 0 || 0 || 0 || 0". Ao olhar o primeiro operando da expressão, "1", ele nem precisa olhar os outros pra saber que o resultado disso é 1. Assim que ele encontra um valor não-nulo em uma sequência de ORs, ele automaticamente assume que este é o valor que deve ser retornado e parte para a próxima instrução. Por inferência, podemos concluir que
my $var = 0 || 0 || 1 || 0 || 0 || 0 || 0;
faria o Perl olhar para 3 operandos na expressão lógica acima. Ao falhar no primeiro, ele prossegue para o segundo, falha neste, prossegue para o terceiro, vê que ele é verdadeiro e termina.
Podemos nos aproveitar desta filosofia para controlar o fluxo de nosso programa. Suponha que estejamos escrevendo uma sub, que deve receber um parâmetro, e que não deve prosseguir caso ele não seja passado como argumento. Uma maneira de fazer isso seria:
sub zero{ my $parametro = shift; return unless $parametro; # return if $!parametro; # if (!$parametro) { return; }}
Contudo, podemos usar o or para fazê-lo de forma mais elegante:
my $parametro = shift or return;
De fato o programa funcionaria se usássemos o operador de alta precedência, mas isso faria tanto sentido quanto usar o seguinte código para retornar de uma sub:
sub zero { my $parametro = shift; # codigo da sub... # ... # ... $parametro = return; }
De fato o programa retorna, mas não faz sentido armazenar o retorno do retorno /applications/core/interface/imageproxy/imageproxy.php?img=http://forum.imasters.com.br/public/style_emoticons/default/smile.gif&key=15294d64c22e9e9c4ae0bf82a62ec27d13f27d6ba7078a5f7982077798029364" alt="smile.gif" />
Contudo, se quisermos setar um valor default para $parametro, DEVEMOS usar o de alta precedência:
sub zero { my $parametro = shift || "valor default"; }
Usar "or" aqui faria $parametro ser igual a undef caso o argumento não fosse passado (lembre-se do exemplo de $x or $y).
Conhecendo a diferença das precedências, podemos construir uma estrutura de if's mais elegante. Suponha que queiramos ler uma string de STDIN, compará-la a uma frase e, caso as duas sejam iguais, executar nossa sub zero:
my $frase = 'oi, tudo bem?'; chomp(my $lida = <>); if ($lida eq $frase) { zero(); } # ou, em uma linha: zero() if $lida eq $frase # da-lhe perl.
Funciona. Mas poderíamos fazer melhor:
my $frase = 'oi, tudo bem?'; chomp(my $lida = <>); $lida eq $frase and zero();
Se é a primeira vez que você vê este tipo de construção, maravilhe-se. Evidenciando as operações de acordo com a precedência, teríamos:
($lida eq $frase) and zero();
O perl primeiro checa se a primeira expressão é verdadeira. Caso ela não seja, ele nem precisa testar a segunda. Portanto, a chamada de zero() não é executada caso $lida ne $frase.
O mesmo pode ser feito para chamar zero() se a frase não for igual a uma estipulada ("a não ser que"). Primeiro do jeito mais comum:
my $frase_proibida = 'alguma blasfemia'; chomp(my $lida = <>); if ($lida ne $frase_proibida) { zero(); }
E aproveitando o lazyeval:
my $frase_proibida = 'alguma blasfemia'; chomp(my $lida = <>); $lida eq $fase_proibida or zero();
Da mesma forma, se $lida eq $frase_proibida, o Perl nem precisa testar o valor retornado por sub zero, então a chamada não é nem realizada. Note que, mais uma vez, usar o operador de alta precedência resultaria em bug:
my $frase_proibida = 'alguma blasfemia'; chomp(my $lida = <>); $lida eq $fase_proibida || zero();
Neste caso, a precedência evidenciada nos daria:
$lida eq ($frase_proibida OR zero());
sub zero só seria chamada se $fase_proibida não tivesse sido inicializada. Caso contrario, independente da entrada fornecida, a chamada seria ignorada e nosso programa estaria quebrado.
----
Por hoje é só. Quem tiver alguma dúvida, ou avistar algum erro que eu tenha cometido, por favor fale comigo aqui mesmo. Espero ter dado uma introdução boa o suficiente pra que quem esteja iniciando em Perl consiga prosseguir /applications/core/interface/imageproxy/imageproxy.php?img=http://forum.imasters.com.br/public/style_emoticons/default/wink.gif&key=0566fd943552bcff9cb1b879403ca34b5ff8f67befaac7fe4648006e9f764689" alt="wink.gif" />
Carregando comentários...