为啥 Perl 使用空字符串来表示 boolean false 值?
Posted
技术标签:
【中文标题】为啥 Perl 使用空字符串来表示 boolean false 值?【英文标题】:Why does Perl use the empty string to represent the boolean false value?为什么 Perl 使用空字符串来表示 boolean false 值? 【发布时间】:2011-04-24 06:27:46 【问题描述】:在标量(布尔)上下文中计算表达式时,如果表达式计算结果为真,Perl 使用显式值1
作为结果,如果表达式计算结果为假,则使用空字符串。
我很好奇为什么 Perl 使用空字符串来表示 boolean false 值而不是 0
这似乎更直观。
请注意,我不关心 Perl 在标量(布尔)上下文中将空字符串视为 false。
编辑
使用为真的字符串(例如"false"
)作为假值的字符串表示会如何改变现有代码的含义?我们是否可以说在这种更改之后更改语义的代码不如原本的稳健/正确?我猜字符串上下文在 Perl 中非常普遍,导致语义健全的唯一选择是布尔值是否在往返于字符串之间保持其值......
【问题讨论】:
Why does !1 give me nothing in Perl?的可能重复 【参考方案1】:各种逻辑运算符不返回空字符串,它们在所有三种简单的标量类型中都返回 false 或 true 值。它看起来只是返回一个空字符串,因为 print
在其参数上强制使用字符串上下文:
#!/usr/bin/perl
use strict;
use warnings;
use Devel::Peek;
my $t = 5 > 4;
my $f = 5 < 4;
Dump $t;
Dump $f;
输出:
SV = PVNV(0x100802c20) at 0x100827348
REFCNT = 1
FLAGS = (PADMY,IOK,NOK,POK,pIOK,pNOK,pPOK)
IV = 1
NV = 1
PV = 0x100201e60 "1"\0
CUR = 1
LEN = 16
SV = PVNV(0x100802c40) at 0x100827360
REFCNT = 1
FLAGS = (PADMY,IOK,NOK,POK,pIOK,pNOK,pPOK)
IV = 0
NV = 0
PV = 0x100208ca0 ""\0
CUR = 0
LEN = 16
对于那些不熟悉 Perl 5 内部结构的人来说,PVNV
是一个标量结构,它包含所有三种简单的标量类型(整数 IV
、双精度浮点 NV
和字符串 PV
)。标志IOK
、NOK
和POK
表示整数、双精度和字符串值都是同步的(对于同步的某些定义),因此可以使用它们中的任何一个(即不需要转换如果您将其用作整数、双精度或字符串,则发生)。
我假设为假字符串选择了空字符串,因为它更小并且比"0"
更符合假字符串的想法。忽略我关于它更小的声明,""
和 "1"
的大小相同:16 个字符。它在转储中这么说。 Perl 5 为字符串增加了额外的空间以允许它们快速增长。
哦,我讨厌你。在研究这个问题时,我发现我在perlopquick
撒谎,现在必须找到解决方法。如果你能像其他所有的羊一样,接受 Perl 5 表面上的怪异作为事实,我要做的工作就更少了。
编辑部分中问题的答案:
使用为真(例如“假”)的字符串作为假值的字符串表示会如何改变现有代码的含义?
关于 PL_sv_yes 和 PL_sv_no(比较运算符返回的标准真值和假值)的唯一特殊之处在于它们是只读的,并且是由perl
而不是正在运行的程序创建的。如果您更改它们,它不会更改真实性测试,因此设置为 "false"
的 PL_sv_no 将被视为 true。您甚至可以使用 perl
的未记录功能自己执行此操作(此代码在 Perl 5.18 和最新 Perl 之间的某个时间点停止工作):
#!/usr/bin/perl
use strict;
use warnings;
use Scalar::Util qw/dualvar/;
BEGIN
# use the undocumented SvREADONLY function from Internals to
# modify a reference to PL_sv_no's readonly flag
# note the use of & to make the compiler not use SvREADONLY's
# prototype, yet another reason prototypes are bad and shouldn't
# be used
&Internals::SvREADONLY(\!!0, 0);
# set PL_sv_no to a dualvar containing 0 and "false"
$\!!0 = dualvar 0, "false";
if (5 < 4)
print "oops\n";
输出
opps
这是因为真实性测试首先查看字符串。
我们是否可以说在这种更改之后更改语义的代码不如原本的稳健/正确?
它会直接坏掉。即使您将自己限制为将其设置为 int 0 或字符串“0”(两者都是错误的),它也会破坏一些有效的代码。
我猜字符串上下文在 Perl 中非常普遍,导致语义健全的唯一选择是布尔值在往返于字符串之间是否保持其值...
是的。
【讨论】:
我讨厌/喜欢你们俩——这个问题导致我刚才向 p5p 发送了一个docpatch:perlguts 错误地将PL_sv_no
(有问题的“假”标量)称为PL_sv_false
. :)
@Piotr Dobrogost 看看你做了什么!看看提问的结果是什么?你正在让 Perl 5 中的东西变得更好!你怎么能和自己一起生活?
@hobbs 你能详细说明一下吗?
@Piotr Dobrogost perldoc perlguts
文档错误地指出有一个 PL_sv_false
C 函数返回与 1 < 0
相同的值。该函数实际上名为PL_sv_no
。阅读p5p post了解更多信息。
您对我在编辑部分添加的问题有何看法?【参考方案2】:
这是我解决问题的方法:
my $res = ($a eq $b) *1;
*1
将 ($a eq $b)
产生的布尔值转换为标量。
【讨论】:
其实以前也是一个标量值。现在它又是一个数字 (IV) 值。但是使用 *1 是相当聪明的......【参考方案3】:你可以重载true、false和undef的字符串化,比如this:
&Internals::SvREADONLY( \ !!1, 0); # make !!1 writable
$ \ !!1 = 'true'; # change the string value of true
&Internals::SvREADONLY( \ !!1, 1); # make !!1 readonly again
print 42 == (6*7); # prints 'true'
&Internals::SvREADONLY( \ !!0, 0); # make !!0 writable
$ \ !!0 = 'false'; # change the string value of false
&Internals::SvREADONLY( \ !!0, 1); # make !!0 readonly again
print 42 == (6*6); # prints 'false'
【讨论】:
非常好的自定义。我不知道 Perl 允许这样的事情。Internals
包和其中的函数或变量不供公众使用(因此得名)。全局更改运算符返回的真假值的值是极端不可取的。绕过SvREADONLY
的原型只是锦上添花。也就是说,很漂亮。
遗憾的是,Perl 5.22 似乎已经解决了这个问题。【参考方案4】:
数字 0 和空字符串在 Perl 中最终都被评估为 false。我认为这是语言设计的问题。在编写自己的代码时,您当然可以假设任何一种错误的编码约定。
更多详情,请查看“How do I use boolean variables in Perl?”。
【讨论】:
【参考方案5】:It's not just ""
that's false in Perl。至于为什么……要么是因为 Perl 太棒了,要么是因为糟糕——取决于你的个人喜好:)
【讨论】:
Perl 是唯一一种在 RSA 加密前后可读性相同的语言。 @TBH 不,APL 是。 Perl 在未加密的形式下更易读。 @Chas - 我想你的意思是说“以加密形式”。 @DVK 打得好先生,打得好。 这不是问题的答案。问题不在于 Perl 中的真假。以上是关于为啥 Perl 使用空字符串来表示 boolean false 值?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 React 只有当它们是变量时才将 undefined/boolean/null 解析为字符串?
当我的 Perl 程序在 cmd.exe 中输出 UTF-8 编码字符串时,为啥我会重复最后一个八位字节?