Как Perl решает рассматривать скаляр как строку или число?

StackOverflow https://stackoverflow.com/questions/330878

  •  11-07-2019
  •  | 
  •  

Вопрос

Рассмотрим следующий код и его вывод:

Код

#!/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;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top