Comment Perl décide-t-il de traiter un scalaire comme une chaîne ou un nombre?
Question
Considérez le code suivant et sa sortie:
Code
#!/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 );
Sortie
$VAR1 = 24;
$VAR1 = '1.7';
$VAR1 = 1000;
$VAR1 = 480;
Comme vous pouvez le constater, la deuxième variable est traitée comme une chaîne, tandis que la première et la quatrième sont traitées comme un nombre. Quelqu'un a une idée de la logique sous-jacente?
Modifier les calculs arithmétiques ajoutés ne résolvent pas complètement le problème (voir 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
La solution
Le travail de Data :: Dumper consiste à sérialiser des données et vous ne pouvez pas en dire beaucoup sur ce que perl fait en interne avec les données en fonction de leur sortie. Le module Devel :: Peek peut vider les indicateurs et les valeurs sous-jacents stockés dans les variables. Le POD Devel :: Peek explique l’importance des drapeaux.
#!/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
Autres conseils
Tout votre concept selon lequel Perl considère les variables comme des chaînes ou des nombres est erroné. Perl traitera vos variables de la bonne façon lorsque vous en aurez besoin, par exemple, les opérateurs arithmétiques always traiteront leurs arguments comme des nombres (en supposant que vous n'abusiez pas de la surcharge d'opérateurs ou autres).
Ne vous inquiétez pas pour ça: Perl sait ce qu'il fait.
Data :: Dumper utilise un modèle simple sur la représentation sous forme de chaîne de la variable pour déterminer s’il s’agit d’un nombre. À partir du code source:
...
elsif ($val =~ /^(?:0|-?[1-9]\d{0,8})\z/) { # safe decimal number
$out .= $val;
}
else { # string
...
Cela ne correspond pas aux nombres avec une virgule décimale qui explique le comportement observé.
Façon rapide et sale de forcer un contexte numérique:
print Dumper( $HOURS_DURATION + 0.0 );
Si votre préoccupation est de savoir comment les données seront affichées, la méthode la plus simple consiste à: -
printf "%5.2d",$HOURS_DURATION;