为什么Perl使用空字符串来表示布尔值false?

时间:2021-12-22 19:15:56

When evaluating an expression in a scalar (boolean) context, Perl uses the explicit value 1 as a result if the expression evaluates to true and the empty string if the expression evaluates to false. I'm curious why Perl uses the empty string to represent boolean false value and not 0 which seems more intuitive.

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

Note that I'm not concerned with Perl treating the empty string as a false in scalar (boolean) context.

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

EDIT

编辑

How would using string which is true ("false" for instance) as a string representation of false values change the meaning of existing code? Could we say that code that changes semantics after such a change is less robust/correct than it could have been? I guess string context is so pervasive in Perl that the only option leading to sane semantics is if boolean value preserve its value after round tripping to and from a string...

如何使用true的字符串(例如“false”)作为false值的字符串表示会改变现有代码的含义?我们可以说在这种变化之后改变语义的代码不如它本来的那么健壮/正确吗?我认为字符串上下文在Perl中是如此普遍,以至于导致理智语义的唯一选择是,如果布尔值在向字符串转换为字符串之后保留其值...

5 个解决方案

#1


30  

The various logical operators don't return an empty string, they return a false or true value in all three simple scalar types. It just looks like it returns an empty string because print forces a string context on its arguments:

各种逻辑运算符不返回空字符串,它们在所有三种简单标量类型中返回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;

Output:

输出:

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

For those not familiar with the Perl 5 internals, a PVNV is a scalar structure that holds all three simple scalar types (integer IV, double precision float NV, and string PV). The flags IOK, NOK, and POK mean that the integer, double, and string values are all in sync (for some definition of in sync) so any one of them may be used (i.e. no conversions need to take place if you use it as an integer, double, or string).

对于那些不熟悉Perl 5内部的人来说,PVNV是一个标量结构,它包含所有三种简单的标量类型(整数IV,双精度浮点NV和字符串PV)。标志IOK,NOK和POK意味着整数,双精度和字符串值都是同步的(对于某些同步定义),因此可以使用它们中的任何一个(即,如果使用它,则不需要进行转换)作为整数,双精度或字符串)。

I assume the empty string was chosen for the false string because it is smaller and is more in keeping with the idea of a false string than "0". Ignore my statement about it being smaller, both "" and "1" are the same size: sixteen characters. It says so right in the dump. Perl 5 adds extra space to strings to allow them to grow quickly.

我假设为假字符串选择了空字符串,因为它更小并且更符合假字符串而不是“0”的想法。忽略我关于它变小的说法,“”和“1”都是相同的大小:十六个字符。它在转储中说得很对。 Perl 5为字符串增加了额外的空间,使它们能够快速增长。

Oh, and I hate you. In researching this I have found that I have lied in perlopquick and will now have to find a way to fix it. If only you had been like all of the other sheep and just accepted Perl 5's surface weirdness as fact, I would have less work to do.

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

Answers to the questions in the EDIT section:

编辑部分中的问题答案:

How would using string which is true ("false" for instance) as a string representation of false values change the meaning of existing code?

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

The only special things about about PL_sv_yes and PL_sv_no (the canonically true and false values returned by comparison operators) are that they are read only and are created by perl not the program that is running. If you change them, it does not change the truthiness test, so a PL_sv_no that is set to "false" will be treated as true. You can even do this yourself (this code stops working at some point between Perl 5.18 and the latest Perl) using undocumented features of perl:

关于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";
}

outputs

输出

opps

This is because the truthiness test looks at strings first.

这是因为真实性测试首先查看字符串。

Could we say that code that changes semantics after such a change is less robust/correct than it could have been?

我们可以说在这种变化之后改变语义的代码不如它本来的那么健壮/正确吗?

It will be straight up broken. Even if you restrict yourself to setting it to an int 0 or a string "0" (both of which are false), it will break some valid code.

它会被打破。即使你限制自己设置为int 0或字符串“0”(两者都是false),它也会破坏一些有效的代码。

I guess string context is so pervasive in Perl that the only option leading to sane semantics is if boolean value preserve its value after round tripping to and from a string...

我认为字符串上下文在Perl中是如此普遍,以至于导致理智语义的唯一选择是,如果布尔值在向字符串转换为字符串之后保留其值...

Yes.

是。

#2


5  

You can overload the stringification of true, false and undef, like this:

你可以重载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'

#3


2  

It's not just "" that's false in Perl. As for why... it's either because Perl is awesome or terrible -- depending on your personal preferences :)

在Perl中,这不仅仅是“”。至于为什么......这要么是因为Perl很棒或很糟糕 - 取决于你的个人喜好:)

#4


2  

Both number 0 and empty string ultimately evaluate as false in Perl. I think this is a matter of language design. When writing your own code, you can of course assume any which one false encoding convention.

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

For further details, check out "How do I use boolean variables in Perl?".

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

#5


1  

Here is how I got around the problem:

以下是我解决问题的方法:

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

The *1 converts the boolean resulting from ($a eq $b) into a scalar.

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

#1


30  

The various logical operators don't return an empty string, they return a false or true value in all three simple scalar types. It just looks like it returns an empty string because print forces a string context on its arguments:

各种逻辑运算符不返回空字符串,它们在所有三种简单标量类型中返回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;

Output:

输出:

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

For those not familiar with the Perl 5 internals, a PVNV is a scalar structure that holds all three simple scalar types (integer IV, double precision float NV, and string PV). The flags IOK, NOK, and POK mean that the integer, double, and string values are all in sync (for some definition of in sync) so any one of them may be used (i.e. no conversions need to take place if you use it as an integer, double, or string).

对于那些不熟悉Perl 5内部的人来说,PVNV是一个标量结构,它包含所有三种简单的标量类型(整数IV,双精度浮点NV和字符串PV)。标志IOK,NOK和POK意味着整数,双精度和字符串值都是同步的(对于某些同步定义),因此可以使用它们中的任何一个(即,如果使用它,则不需要进行转换)作为整数,双精度或字符串)。

I assume the empty string was chosen for the false string because it is smaller and is more in keeping with the idea of a false string than "0". Ignore my statement about it being smaller, both "" and "1" are the same size: sixteen characters. It says so right in the dump. Perl 5 adds extra space to strings to allow them to grow quickly.

我假设为假字符串选择了空字符串,因为它更小并且更符合假字符串而不是“0”的想法。忽略我关于它变小的说法,“”和“1”都是相同的大小:十六个字符。它在转储中说得很对。 Perl 5为字符串增加了额外的空间,使它们能够快速增长。

Oh, and I hate you. In researching this I have found that I have lied in perlopquick and will now have to find a way to fix it. If only you had been like all of the other sheep and just accepted Perl 5's surface weirdness as fact, I would have less work to do.

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

Answers to the questions in the EDIT section:

编辑部分中的问题答案:

How would using string which is true ("false" for instance) as a string representation of false values change the meaning of existing code?

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

The only special things about about PL_sv_yes and PL_sv_no (the canonically true and false values returned by comparison operators) are that they are read only and are created by perl not the program that is running. If you change them, it does not change the truthiness test, so a PL_sv_no that is set to "false" will be treated as true. You can even do this yourself (this code stops working at some point between Perl 5.18 and the latest Perl) using undocumented features of perl:

关于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";
}

outputs

输出

opps

This is because the truthiness test looks at strings first.

这是因为真实性测试首先查看字符串。

Could we say that code that changes semantics after such a change is less robust/correct than it could have been?

我们可以说在这种变化之后改变语义的代码不如它本来的那么健壮/正确吗?

It will be straight up broken. Even if you restrict yourself to setting it to an int 0 or a string "0" (both of which are false), it will break some valid code.

它会被打破。即使你限制自己设置为int 0或字符串“0”(两者都是false),它也会破坏一些有效的代码。

I guess string context is so pervasive in Perl that the only option leading to sane semantics is if boolean value preserve its value after round tripping to and from a string...

我认为字符串上下文在Perl中是如此普遍,以至于导致理智语义的唯一选择是,如果布尔值在向字符串转换为字符串之后保留其值...

Yes.

是。

#2


5  

You can overload the stringification of true, false and undef, like this:

你可以重载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'

#3


2  

It's not just "" that's false in Perl. As for why... it's either because Perl is awesome or terrible -- depending on your personal preferences :)

在Perl中,这不仅仅是“”。至于为什么......这要么是因为Perl很棒或很糟糕 - 取决于你的个人喜好:)

#4


2  

Both number 0 and empty string ultimately evaluate as false in Perl. I think this is a matter of language design. When writing your own code, you can of course assume any which one false encoding convention.

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

For further details, check out "How do I use boolean variables in Perl?".

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

#5


1  

Here is how I got around the problem:

以下是我解决问题的方法:

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

The *1 converts the boolean resulting from ($a eq $b) into a scalar.

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