有人能解释一下 Perl 中字符串“0 but true”的确切含义吗?据我了解,它在整数比较中等于零,但在用作布尔值时评估为真。它是否正确?这是语言的正常行为,还是在解释器中被视为特殊情况的特殊字符串?
14 回答
这是语言的正常行为。引用手册perlsyn
页:
在布尔上下文中,数字
0
、字符串'0'
和""
、空列表()
和undef
都是假的。所有其他值都为真。取反一个真值!
或not
返回一个特殊的假值。当评估为字符串时,它被视为""
,但作为数字,它被视为0
。
因此,需要有一种方法可以0
从期望返回0
(成功的)返回值的系统调用中返回,并留下一种通过实际返回错误值来指示失败情况的方法。"0 but true"
服务于这个目的。
因为它在 Perl 核心中被硬编码为将其视为数字。这是使 Perl 的约定和ioctl
的约定一起发挥作用的 hack;来自perldoc -f ioctl
:
ioctl
(and )的返回值fcntl
如下:if OS returns: then Perl returns: -1 undefined value 0 string "0 but true" anything else that number
因此 Perl 在成功时返回 true,在失败时返回 false,但您仍然可以轻松确定操作系统返回的实际值:
$retval = ioctl(...) || -1; printf "System returned %d\n", $retval;
特殊字符串
"0 but true"
免于-w
对不正确的数字转换的投诉。
除了其他人所说的之外,"0 but true"
它的特殊情况是它不会在数字上下文中发出警告:
$ perl -wle 'print "0 but true" + 3'
3
$ perl -wle 'print "0 but crazy" + 3'
Argument "0 but crazy" isn't numeric in addition (+) at -e line 1.
3
该值0 but true
是 Perl 中的一种特殊情况。尽管在你的凡人眼中,它看起来不像一个数字,但明智且众所周知的 Perl 明白它确实是一个数字。
这与当 Perl 子例程返回 0 值时,假定例程失败或返回错误值有关。
想象一下,我有一个返回两个数字之和的子程序:
die "You can only add two numbers\n" if (not add(3, -2));
die "You can only add two numbers\n" if (not add("cow", "dog"));
die "You can only add two numbers\n" if (not add(3, -3));
第一条语句不会死,因为子例程将返回一个1
. 那挺好的。第二条语句将终止,因为子例程无法将cow添加到dog。
还有,第三个说法?
嗯,我可以3
补充-3
。我刚刚得到0
,但是即使add
子例程有效,我的程序也会死掉!
为了解决这个问题,Perl 认为0 but true
是一个数字。如果我的add子例程不仅返回0,而且返回0 而是 true,我的第三条语句将起作用。
但是0 but true是数字零吗?试试这些:
my $value = "0 but true";
print qq(Add 1,000,000 to it: ) . (1_000_000 + $value) . "\n";
print "Multiply it by 1,000,000: " . 1_000_000 * $value . "\n";
是的,它是零!
index子例程是 Perl 的一个非常古老的部分,在 0 概念之前就存在,但true就在附近。假设返回位于字符串中的子字符串的位置:
index("barfoo", "foo"); #This returns 3
index("barfoo", "bar"); #This returns 0
index("barfoo", "fu"); #This returns ...uh...
最后一条语句返回一个-1
. 这意味着如果我这样做:
if ($position = index($string, $substring)) {
print "It worked!\n";
}
else {
print "If failed!\n";
}
正如我通常使用标准函数所做的那样,它不起作用。如果我像在第二个语句中那样使用“barfoo”和“bar”,则该else
子句将执行,但如果我在第三个语句中使用“barfoo”和“fu”,则该if
子句将执行。不是我想要的。
但是,如果子例程为第二个语句和第三个语句index
返回0 但为 trueundef
,则my if
/else
子句将起作用。
您可能还会看到 Perl 代码中使用的字符串“0E0”,它的含义相同,其中 0E0 仅表示用指数表示法编写的 0。然而,由于 Perl 只认为 "0"、'' 或 undef 为假,它在布尔上下文中计算为真。
它在 Perl 的源代码中被硬编码,特别是在numeric.c 的 Perl_grok_number_flags 中。
阅读该代码后,我发现字符串“infinity”(不区分大小写)也通过了looks_like_number 测试。我不知道。
在整数上下文中,它的计算结果为 0(字符串开头的数字部分)并且为零。在标量上下文中,它是一个非空值,所以它是真的。
if (int("0 but true")) { print "zero"; }
(无输出)
if ("0 but true") { print "true"; }
(打印为真)
在 Perl(和其他与 C 相关的语言)中,0表示 false。在大多数情况下,这是一种合理的行为。其他语言(例如 Lua)将0视为 true 并提供另一个标记(通常为nil或false)来表示非 true 值。
Perl 方法不能很好地工作的一种情况是,当您想要返回一个数字,或者如果函数由于某种原因失败,则返回一个 false 值。例如,如果您编写一个从文件中读取一行并返回该行字符数的函数。该函数的常见用法可能类似于:
while($c = characters_in_line($file)){
...
};
请注意,如果特定行上的字符数为 0,则while循环将在文件末尾之前结束。因此characters_in_line函数应该将 0 个字符特例并返回'0 but true'代替。这样,该函数将在while循环中按预期工作,但如果将其用作数字,也会返回正确答案。
请注意,这不是语言的内置部分。相反,它利用了 Perl 将字符串解释为数字的能力。所以有时会使用其他的刺来代替。 例如, DBI使用"0E0"。在数字上下文中评估时,它们返回0,但在布尔上下文中返回false。
虚假的东西:
""
."0"
.- 那些串起来的东西。
"0 but true"
不是其中之一,所以它不是假的。
此外,Perl 返回"0 but true"
期望数字的位置,以表明函数成功,即使它返回零。sysseek就是这种功能的一个例子。由于该值预计将用作数字,因此 Perl 被编码为将其视为数字。因此,当它用作数字时不会发出警告,并looks_like_number("0 but true")
返回 true。
其他“真零”可以在http://www.perlmonks.org/?node_id=464548找到。
“0 但为真”的另一个例子:
DBI 模块使用“0E0”作为不影响任何记录的 UPDATE 或 DELETE 查询的返回值。它在布尔上下文中计算为 true(表示查询已正确执行),在数字上下文中计算为 0,表示查询没有更改任何记录。
我刚刚发现了字符串“0 but true”实际上内置在解释器中的证据,就像这里的一些人已经回答过的那样:
$ strings /usr/lib/perl5/5.10.0/linux/CORE/libperl.so | grep -i true
Perl_sv_true
%-p did not return a true value
0 but true
0 but true
当您要编写一个返回整数值、false或undef的函数时(即对于错误情况),您必须注意值为零。返回它是假的,不应指示错误情况,因此返回“0 但真”使函数返回值为真,同时在对其进行数学运算时仍将值传回零。
"0 but true" 和其他字符串一样,但由于 perl 的语法,它可以提供有用的目的,即从函数返回整数零而不结果为 "false"(在 perl 眼中)。
并且字符串不必是“0 但为真”。“0 但假”在布尔意义上仍然是“真”。
考虑:
if(x)
for x: yields:
1 -> true
0 -> false
-1 -> true
"true" -> true
"false" -> true
"0 but true" -> true
int("0 but true") ->false
所有这一切的结果是您可以拥有:
sub find_x()
并让此代码能够打印“0”作为其输出:
if($x = find_x)
{
print int($x) . "\n";
}
字符串 ``0 but true'' 仍然是一个特例:
for arg in "'0 but true'" "1.0*('0 but true')" \
"1.0*('0 but false')" 0 1 "''" "0.0" \
"'false'" "'Ja'" "'Nein'" "'Oui'" \
"'Non'" "'Yes'" "'No'" ;do
printf "%-32s: %s\n" "$arg" "$(
perl -we '
my $ans=eval $ARGV[0];
$ans=~s/^(Non?|Nein)$//;
if ($ans) {
printf "true: |%s|\n",$ans
} else {
printf "false: |%s|", $ans
};' "$arg"
)"
done
给出以下内容:(注意“警告”!)
'0 but true' : true: |0 but true|
1.0*('0 but true') : false: |0|
Argument "0 but false" isn't numeric in multiplication (*) at (eval 1) line 1.
1.0*('0 but false') : false: |0|
0 : false: |0|
1 : true: |1|
'' : false: ||
0.0 : false: |0|
'false' : true: |false|
'Ja' : true: |Ja|
'Nein' : false: ||
'Oui' : true: |Oui|
'Non' : false: ||
'Yes' : true: |Yes|
'No' : false: ||
...并且不要忘记 RTFM!
man -P'less +"/0 but [a-z]*"' perlfunc
... "fcntl". Like "ioctl", it maps a 0 return from the system call
into "0 but true" in Perl. This string is true in boolean
context and 0 in numeric context. It is also exempt from the
normal -w warnings on improper numeric conversions. ...