As alegrias da precidência

A precidência em Perl pode te surpreender …. me pegou!

Tenho um script que envia por e-mail alguns resultados para um assinante diariamente. Se não houver resultados, ele não envia um e-mail ao usuário – você só recebe um e-mail se houver algo para ver, em outras palavras.
Como um lembrete, o script enviará por e-mail um lembrete de inscrição para o usuário no primeiro dia do mês … mas apenas se ele não tiver recebido um e-mail com os resultados naquele dia.

O código era mais ou menos assim:

my $has_emailed = email($recipient, $data);
my @t = localtime;
if ( not $has_emailed && not $t[3] ) {
my $text = get_reminder_message();
email
( $recipient, $text);
}

O problema era que eu estava enviando um lembrete por e-mail todos os dias!

Por quê? Precidência .

O &&é um “e em curto-circuito” e tem uma precedência mais alta que o not. Isso significa que a declaração realmente é lida como:

if ( not ($has_emailed && not $t[3]) )

então, seguindo a lógica, obtemos:

  • $t[3] é o dia do mês, começando em 0 – FALSO para o primeiro dia e VERDADEIRO para todos os dias
  • not $t[3] é, portanto, TRUE no primeiro dia, e FALSE em dias alternados
  • $has_emailed é TRUE se os resultados foram enviados para um usuário, FALSE se não …. estamos assumindo FALSE aqui
  • ($has_emailed && not $t[3]) é, portanto, FALSE
  • not ($has_emailed && not $t[3]) torna-se verdade.

Existem várias soluções:

if ( (not $has_emailed) && (not $t[3]) ) {...} # Be explicit where the not's go
if ( !$has_emailed && !$t[3] ) {...} # ! has a higher precedence that &&
if ( $has_emailed || $t[3] ) {} else {...} # cast the problem into the negative
unless ( $has_emailed || not$t[3] )) {...} # Perl::Critic doesn't like unless