¿Cómo decide Perl tratar un escalar como una cadena o un número?
Pregunta
Considere el siguiente código y su salida:
Código
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
my $HOURS_PER_DAY = 24.0 * 1.0;
my $BSA = 1.7 * 1.0;
my $MCG_PER_MG = 1000.0 * 1.0;
my $HOURS_DURATION = 20.0 * $HOURS_PER_DAY;
my $dummy = $HOURS_PER_DAY * $BSA * $MCG_PER_MG * $HOURS_DURATION;
print Dumper($HOURS_PER_DAY);
print Dumper( $BSA);
print Dumper( $MCG_PER_MG);
print Dumper( $HOURS_DURATION );
Salida
$VAR1 = 24;
$VAR1 = '1.7';
$VAR1 = 1000;
$VAR1 = 480;
Como puede ver, la segunda variable se trata como cadenas, mientras que la primera y la cuarta se tratan como números. ¿Alguien tiene alguna idea de cuál es la lógica subyacente?
Editar los cálculos aritméticos que se agregaron no resuelven completamente el problema (vea la variable $ BSA).
$ perl -v
This is perl, v5.10.0 built for cygwin-thread-multi-64int
(with 6 registered patches, see perl -V for more detail)
Copyright 1987-2007, Larry Wall
Solución
Datos :: El trabajo de Dumper es serializar datos y no se puede decir mucho sobre lo que Perl está haciendo internamente con los datos en función de su salida. El módulo Devel :: Peek puede volcar los indicadores y valores subyacentes almacenados en las variables. El Devel :: Peek POD explica el significado de las banderas.
#!/usr/bin/perl
use warnings;
use strict;
use Devel::Peek;
my $HOURS_PER_DAY = 24.0 * 1.0;
my $BSA = 1.7 * 1.0;
my $MCG_PER_MG = 1000.0 * 1.0;
my $HOURS_DURATION = 20.0 * $HOURS_PER_DAY;
my $dummy = $HOURS_PER_DAY * $BSA * $MCG_PER_MG * $HOURS_DURATION;
Dump($HOURS_PER_DAY);
Dump($BSA);
Dump($MCG_PER_MG);
Dump($HOURS_DURATION);
__END__
SV = PVNV(0xd71ff0) at 0xd87f90
REFCNT = 1
FLAGS = (PADBUSY,PADMY,IOK,NOK,pIOK,pNOK)
IV = 24
NV = 24
PV = 0
SV = PVNV(0xd71fc8) at 0xd87f60
REFCNT = 1
FLAGS = (PADBUSY,PADMY,NOK,pIOK,pNOK)
IV = 1
NV = 1.7
PV = 0
SV = PVNV(0xd72040) at 0xd87f40
REFCNT = 1
FLAGS = (PADBUSY,PADMY,IOK,NOK,pIOK,pNOK)
IV = 1000
NV = 1000
PV = 0
SV = IV(0xd8b408) at 0xd87f30
REFCNT = 1
FLAGS = (PADBUSY,PADMY,IOK,pIOK)
IV = 480
# compare the above output to output without the assignment to $dummy:
SV = IV(0x7b0eb8) at 0x7adf90
REFCNT = 1
FLAGS = (PADBUSY,PADMY,IOK,pIOK)
IV = 24
SV = NV(0x7c7c90) at 0x7adf60
REFCNT = 1
FLAGS = (PADBUSY,PADMY,NOK,pNOK)
NV = 1.7
SV = IV(0x7b13d8) at 0x7adf40
REFCNT = 1
FLAGS = (PADBUSY,PADMY,IOK,pIOK)
IV = 1000
SV = IV(0x7b1408) at 0x7adf30
REFCNT = 1
FLAGS = (PADBUSY,PADMY,IOK,pIOK)
IV = 480
Otros consejos
Todo su concepto de que Perl trata las variables como cadenas o números es defectuoso. Perl tratará sus variables de la manera correcta cuando lo necesite, por ejemplo, los operadores aritméticos siempre tratan sus argumentos como números (suponiendo que no está abusando de la sobrecarga de operadores o algo así).
No debe preocuparse por esto: Perl sabe lo que está haciendo.
Data :: Dumper está usando un patrón simple en la representación de cadena de la variable para determinar si es un número. Del código fuente:
...
elsif ($val =~ /^(?:0|-?[1-9]\d{0,8})\z/) { # safe decimal number
$out .= $val;
}
else { # string
...
Esto no coincide con los números que tienen un punto decimal que explica el comportamiento que observó.
Forma sucia rápida de forzar un contexto numérico:
print Dumper( $HOURS_DURATION + 0.0 );
Si su preocupación es cómo se mostrarán los datos, entonces la forma limpia es: -
printf "%5.2d",$HOURS_DURATION;