我不明白为什么join更改JSON::to_string以下示例中的输出:
#!/usr/bin/perl
use v5.26;
use Data::Dumper;
use JSON;
my @version = (1, 2, 3, 4);
say "version: ", join ".", @version; # comment this line out
$Data::Dumper::Terse = 1;
$Data::Dumper::Indent = 0;
say Dumper(\@version);
say to_json(\@version);
Run Code Online (Sandbox Code Playgroud)
输出与包含该行join:
version: 1.2.3.4
[1,2,3,4]
["1","2","3","4"]
Run Code Online (Sandbox Code Playgroud)
但是用join的输出注释掉该行会to_json突然显示整数而不是字符串,尽管 的输出Data::Dumper仍然相同:
[1,2,3,4]
[1,2,3,4]
Run Code Online (Sandbox Code Playgroud)
当您对数字进行字符串化时,字符串化将与原始编号一起存储在标量中。(你可以在我的答案底部看到一个演示。)
当您对字符串进行数字化时,数字化与原始编号一起存储在标量中。
这是一种优化,因为人们经常不止一次地对标量进行字符串化或数字化。
这对 Perl 来说不是问题,因为 Perl 有强制操作符而不是多态操作符。但它让 JSON 序列化器的作者处于困难的境地,要么需要额外的信息,要么猜测应该使用标量包含的哪些值。
您可以使用$x = 0 + $x;.
您可以使用$x = "$x";.
更详细的回答如下。
Perl 可以自由地将内部格式更改为它认为合适的标量。这通常作为修改标量的一部分来完成。
$x = 123; # $x contains a signed integer
$x += 0.1; # $x contains a float
$x = 2147483647; # $x contains a signed integer
++$x; # $x contains an unsigned integer (on a build with 32-bit ints)
$x = "123"; # $x contains a downgraded string
$x += 0; # $x contains a signed integer
$x = "abc"; # $x contains a downgraded string
$x .= "\x{2660}"; # $x contains an upgraded string
Run Code Online (Sandbox Code Playgroud)
但有时,Perl 会向标量添加第二个值作为优化。
$x = 123; # $x contains a signed integer
$x * 0.1; # $x contains a signed integer AND a float
$x = 123; # $x contains a signed integer
"$x"; # $x contains a signed integer AND a downgraded string
$x = "123"; # $x contains a downgraded string
$x+0; # $x contains a signed integer AND a downgraded string
Run Code Online (Sandbox Code Playgroud)
这些不是您会遇到的唯一双重(或三重)变量。
my $x = !!0; # $x contains a signed integer AND a float AND a downgraded string
"$!"; # $! contains a float (not a signed integer?!) AND a downgraded string
Run Code Online (Sandbox Code Playgroud)
这在 Perl 中不是问题,因为我们使用类型强制操作符(例如==适用于数字,eq适用于字符串)。但是许多其他语言依赖于多态运算符(例如,==可用于比较字符串和比较数字)。[1]
但它确实给 JSON 序列化器带来了一个问题,它们被迫将单一类型分配给标量。如果同时$x包含一个字符串和一个数字,应该使用哪个?
如果标量是字符串化的结果,使用数字将是理想的,但如果标量是数字化的结果,则字符串将是理想的。无法确定这些起源中的哪一个与标量(如果有)有关,因此模块的作者面临着艰难的选择。
理想情况下,他们会提供不同的接口,但这可能会增加复杂性和性能损失。
您可以使用 Devel::Peek's 查看标量的内部结构Dump。相关的线是FLAGS线。
IOKwithout IsUV: 包含一个有符号整数IOKwith IsUV: 包含一个无符号整数NOK: 包含一个浮点数POKwithout UTF8:包含降级的字符串POKwith UTF8: 包含升级后的字符串ROK: 包含参考$ perl -MDevel::Peek -e'$x=123; Dump($x); "$x"; Dump($x);' 2>&1 |
perl -M5.014 -ne'next if !/FLAGS/; say join ",", /\b([INPR]OK|IsUV|UTF8)/g'
IOK
IOK,POK
$ perl -MDevel::Peek -e'$x="123"; Dump($x); 0+$x; Dump($x);' 2>&1 |
perl -M5.014 -ne'next if !/FLAGS/; say join ",", /\b([INPR]OK|IsUV|UTF8)/g'
POK
IOK,POK
Run Code Online (Sandbox Code Playgroud)
好吧,Perl 没有针对不同数字类型的单独运算符,这可能会导致问题(例如 -0 存在浮点数,但不是整数),但这些问题很少遇到。
另一个问题是浮点数的字符串化通常会导致信息丢失。