18

在标量(布尔)上下文中计算表达式时,如果表达式计算结果为真,Perl 使用显式值1作为结果,如果表达式计算结果为假,则使用空字符串。我很好奇为什么 Perl 使用空字符串来表示布尔值,而不是0看起来更直观。

请注意,我不关心 Perl 在标量(布尔)上下文中将空字符串视为 false。

编辑

使用为真("false"例如)的字符串作为假值的字符串表示将如何改变现有代码的含义?我们是否可以说在这种更改之后更改语义的代码不如原本的稳健/正确?我猜字符串上下文在 Perl 中如此普遍,导致语义健全的唯一选择是布尔值是否在往返于字符串之间保持其值......

4

5 回答 5

32

各种逻辑运算符不返回空字符串,它们在所有三种简单的标量类型中都返回 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 内部结构的人来说,aPVNV是一个标量结构,它包含所有三种简单的标量类型(整数IV、双精度浮点数NV和字符串PV)。标志IOKNOKPOK表示整数、双精度和字符串值都是同步的(对于同步的某些定义),因此可以使用它们中的任何一个(即,如果将其用作整数,则无需进行转换、双精度或字符串)。

我假设为假字符串选择了空字符串,因为它"0". 忽略我关于它更小的说法,两者""大小"1"相同:16 个字符。它在转储中这么说。Perl 5 为字符串增加了额外的空间以允许它们快速增长。

哦,我恨你。在研究这个问题时,我发现我撒了谎,perlopquick现在必须找到一种方法来解决它。如果您像所有其他羊一样,只是接受 Perl 5 表面上的怪异作为事实,我要做的工作就会更少。

编辑部分中问题的答案:

使用真字符串(例如“假”)作为假值的字符串表示会如何改变现有代码的含义?

关于 PL_sv_yes 和 PL_sv_no(比较运算符返回的规范真假值)的唯一特殊之处在于它们是只读的,并且perl不是由正在运行的程序创建的。如果您更改它们,它不会更改真实性测试,因此设置为的 PL_sv_no"false"将被视为 true。您甚至可以使用以下未记录的功能自己执行此操作(此代码在 Perl 5.18 和最新 Perl 之间的某个时间点停止工作)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 中如此普遍,导致语义健全的唯一选择是布尔值是否在往返于字符串之间保持其值......

是的。

于 2010-10-12T12:09:35.303 回答
5

您可以重载 true、false 和 undef 的字符串化,如下所示

&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'
于 2010-10-12T16:58:50.087 回答
2

不仅仅是 ""在 Perl 中是错误的。至于为什么……这要么是因为 Perl 太棒了,要么是因为糟糕——取决于你的个人喜好 :)

于 2010-10-12T11:31:36.683 回答
2

在 Perl 中,数字 0 和空字符串最终都被评估为 false。我认为这是语言设计的问题。在编写自己的代码时,您当然可以假设任何一种错误的编码约定。

有关详细信息,请查看“如何在 Perl 中使用布尔变量? ”。

于 2010-10-12T11:33:50.257 回答
1

这是我解决问题的方法:

my $res = ($a eq $b) *1;

*1得到的布尔值转换($a eq $b)为标量。

于 2014-12-03T16:45:15.617 回答