Как Perl решает рассматривать скаляр как строку или число?
Вопрос
Рассмотрим следующий код и его вывод:
Код
#!/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 );
Выход
$VAR1 = 24;
$VAR1 = '1.7';
$VAR1 = 1000;
$VAR1 = 480;
<Ч>
Как видите, вторая переменная обрабатывается как строки, а первая и четвертая - как числа. Кто-нибудь имеет представление о том, что лежит в основе логики?
Изменить арифметические вычисления, которые были добавлены, не полностью решают проблему (см. переменную $ 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
Решение
Data :: Dumper выполняет сериализацию данных, и вы не можете много рассказать о том, что perl делает внутренне с данными на основе их вывода. модуль Devel :: Peek может выводить базовые флаги и значения, хранящиеся в переменных. POD Devel :: Peek объясняет значение флагов.
#!/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
Другие советы
Вся ваша концепция Perl, относящаяся к переменным как к строкам или числам, неверна. Perl будет правильно обрабатывать ваши переменные, когда вам это нужно, например, арифметические операторы всегда принимают их аргументы как числа (при условии, что вы не злоупотребляете перегрузкой операторов или чем-то подобным).
Не беспокойтесь об этом: Perl знает, что делает.
Data :: Dumper использует простой шаблон строкового представления переменной, чтобы определить, является ли это число. Из исходного кода:
...
elsif ($val =~ /^(?:0|-?[1-9]\d{0,8})\z/) { # safe decimal number
$out .= $val;
}
else { # string
...
Это не соответствует числам с десятичной точкой, которые объясняют наблюдаемое вами поведение.
Быстрый грязный способ форсировать числовой контекст:
print Dumper( $HOURS_DURATION + 0.0 );
Если вас беспокоит то, как будут отображаться данные, тогда чистый способ: -
printf "%5.2d",$HOURS_DURATION;