In che modo Perl decide di trattare uno scalare come una stringa o un numero?
Domanda
Considera il codice seguente e il suo output:
Codice
#!/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 );
uscita ??h3>
$VAR1 = 24;
$VAR1 = '1.7';
$VAR1 = 1000;
$VAR1 = 480;
$VAR1 = 24;
$VAR1 = '1.7';
$VAR1 = 1000;
$VAR1 = 480;
Come puoi vedere, la seconda variabile è trattata come stringhe, mentre la prima e la quarta sono trattate come numeri. Qualcuno ha idea di quale sia la logica sottostante?
Modifica i calcoli aritmetici aggiunti non risolvono completamente il problema (vedere la variabile $ 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
Soluzione
Dati :: Il compito di Dumper è di serializzare i dati e non si può dire molto su cosa sta facendo il perl internamente con i dati in base al suo output. Il modulo Devel :: Peek può scaricare i flag e i valori sottostanti memorizzati nelle variabili. Il POD Devel :: Peek spiega il significato delle bandiere.
#!/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
Altri suggerimenti
L'intero concetto di Perl che tratta le variabili come stringhe o numeri è difettoso. Perl tratterà le tue variabili nel modo giusto quando ne avrai bisogno, ad esempio gli operatori aritmetici sempre considerano i loro argomenti come numeri (supponendo che tu non stia abusando del sovraccarico dell'operatore o alcuni di questi).
Non dovresti preoccuparti di questo: Perl sa cosa sta facendo.
Dati :: Dumper sta usando un modello semplice sulla rappresentazione di stringa della variabile per determinare se si tratta di un numero. Dal codice sorgente:
...
elsif ($val =~ /^(?:0|-?[1-9]\d{0,8})\z/) { # safe decimal number
$out .= $val;
}
else { # string
...
Questo non corrisponde ai numeri che hanno un punto decimale che spiega il comportamento che hai osservato.
Modo sporco veloce per forzare un contesto numerico:
print Dumper( $HOURS_DURATION + 0.0 );
Se la tua preoccupazione è come verranno visualizzati i dati, allora il modo più pulito è: -
printf "%5.2d",$HOURS_DURATION;