Como posso saber se uma variável tem um valor numérico em Perl?
Pergunta
Existe uma maneira simples em Perl que me permita determinar se uma determinada variável é numérica?Algo como:
if (is_number($x))
{ ... }
seria o ideal.Uma técnica que não lançará avisos quando o -w
switch está sendo usado é certamente preferido.
Solução
Usar Scalar::Util::looks_like_number()
que usa a função looks_like_number() da API Perl C interna, que é provavelmente a maneira mais eficiente de fazer isso.Observe que as strings “inf” e “infinity” são tratadas como números.
Exemplo:
#!/usr/bin/perl
use warnings;
use strict;
use Scalar::Util qw(looks_like_number);
my @exprs = qw(1 5.25 0.001 1.3e8 foo bar 1dd inf infinity);
foreach my $expr (@exprs) {
print "$expr is", looks_like_number($expr) ? '' : ' not', " a number\n";
}
Fornece esta saída:
1 is a number
5.25 is a number
0.001 is a number
1.3e8 is a number
foo is not a number
bar is not a number
1dd is not a number
inf is a number
infinity is a number
Veja também:
- perldoc Escalar::Util
- perldoc perlapi para
looks_like_number
Outras dicas
Confira o módulo CPAN Regexp::Comum.Acho que ele faz exatamente o que você precisa e lida com todos os casos extremos (por exemplo,números reais, notação científica, etc).por exemplo.
use Regexp::Common;
if ($var =~ /$RE{num}{real}/) { print q{a number}; }
A questão original era como saber se uma variável era numérica, e não se "possui um valor numérico".
Existem alguns operadores que possuem modos de operação separados para operandos numéricos e de string, onde "numérico" significa qualquer coisa que era originalmente um número ou que já foi usado em um contexto numérico (por exemplo,em $x = "123"; 0+$x
, antes da adição, $x
é uma string, depois é considerada numérica).
Uma maneira de saber é esta:
if ( length( do { no warnings "numeric"; $x & "" } ) ) {
print "$x is numeric\n";
}
Uma resposta simples (e talvez simplista) à pergunta é o conteúdo de $x
numérico é o seguinte:
if ($x eq $x+0) { .... }
Faz uma comparação textual do original $x
com o $x
convertido em um valor numérico.
Normalmente a validação numérica é feita com expressões regulares.Este código determinará se algo é numérico e também verificará variáveis indefinidas para não gerar avisos:
sub is_integer {
defined $_[0] && $_[0] =~ /^[+-]?\d+$/;
}
sub is_float {
defined $_[0] && $_[0] =~ /^[+-]?\d+(\.\d+)?$/;
}
Aqui estão alguns material de leitura você deveria olhar.
Não é perfeito, mas você pode usar um regex:
sub isnumber
{
shift =~ /^-?\d+\.?\d*$/;
}
Não acredito que haja algo embutido para fazer isso.Para saber mais do que você sempre quis ver sobre o assunto, veja Perlmonks na detecção numérica
Uma regex um pouco mais robusta pode ser encontrada em Regexp::Comum.
Parece que você quer saber se Perl acha que uma variável é numérica.Aqui está uma função que captura esse aviso:
sub is_number{
my $n = shift;
my $ret = 1;
$SIG{"__WARN__"} = sub {$ret = 0};
eval { my $x = $n + 1 };
return $ret
}
Outra opção é desligar o aviso localmente:
{
no warnings "numeric"; # Ignore "isn't numeric" warning
... # Use a variable that might not be numeric
}
Observe que variáveis não numéricas serão convertidas silenciosamente para 0, o que provavelmente é o que você queria.
rexep não é perfeito...isso é:
use Try::Tiny;
sub is_numeric {
my ($x) = @_;
my $numeric = 1;
try {
use warnings FATAL => qw/numeric/;
0 + $x;
}
catch {
$numeric = 0;
};
return $numeric;
}
Experimente isto:
If (($x !~ /\D/) && ($x ne "")) { ... }
mas achei isso interessante
if ( $value + 0 eq $value) {
# A number
push @args, $value;
} else {
# A string
push @args, "'$value'";
}
Pessoalmente, acho que o caminho a seguir é confiar no contexto interno do Perl para tornar a solução à prova de balas.Um bom regexp poderia corresponder a todos os valores numéricos válidos e a nenhum dos não numéricos (ou vice-versa), mas como existe uma maneira de empregar a mesma lógica que o intérprete está usando, seria mais seguro confiar diretamente nisso.
Como costumo executar meus scripts com -w
, tive que combinar a ideia de comparar o resultado de "valor mais zero" ao valor original com o no warnings
abordagem baseada em @ysth:
do {
no warnings "numeric";
if ($x + 0 ne $x) { return "not numeric"; } else { return "numeric"; }
}
Você pode usar expressões regulares para determinar se $foo é um número (ou não).
Dê uma olhada aqui:Como posso determinar se um escalar é um número
if (definido $ x && $ x! ~ m/ d/) {} ou $ x = 0 se!$x;se ($x!~m/\D/) {}
Esta é uma ligeira variação da resposta de Veekay, mas deixe-me explicar meu raciocínio para a mudança.
Executar um regex em um valor indefinido causará a ocorrência de erros e fará com que o código saia em muitos, se não na maioria dos ambientes.Testar se o valor está definido ou definir um caso padrão como fiz no exemplo alternativo antes de executar a expressão salvará, no mínimo, seu log de erros.
Esta função funciona para mim:
sub IS_Integer()
{
my $Text = shift;
my $Integer = 0;
if ($Text =~ /\D/)
{
$Integer = 1;
}
if ($Text =~ /^\d+$/)
{
$Integer = 1;
}
if ($Text =~ /^-?\d+$/)
{
$Integer = 1;
}
if ($Text =~ /^[+-]?\d+$/)
{
$Integer = 1;
}
if ($Text =~ /^-?\d+\.?\d*$/)
{
$Integer = 1;
}
if ($Text =~ /^-?(?:\d+(?:\.\d*)?&\.\d+)$/)
{
$Integer = 1;
}
if ($Text =~ /^([+-]?)(?=\d&\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/)
{
$Integer = 1;
}
return $Integer;
}