Kra*_*lew 7 perl boolean undef
简短的问题
在Perl代码库中一致表示真假的最佳方法是什么?
1/ 0?
1/ Perl的本机布尔运算符
返回的特殊空字符串?
undef?
() (即空列表)?
问题背景
我们都知道Perl在布尔方面非常灵活,就像大多数事情一样.
例如,Perl将以下内容视为false:,undef()数字0(即使写为000或0.0),空字符串'0'(包含单个0位的字符串).Perl将以下作为真:任何其他的标量值,1,-1,在它(一个空格的字符串' '),'00'多个0的串,"0\n"(跟着一个新行"0"), , 'true','false','undef'等等另外,基于阵列to-scalar和list-to-scalar转换意味着你可以经常使用空数组作为假值.(感谢http://perlmaven.com/boolean-values-in-perl获取Perl接受为真或假的标量值的部分列表.)
但是考虑到所有这些被视为true或false的值,Perl布尔函数应该返回什么?
1/0-1/0 (全息)1/undef1/""如果您使用这些约定中的任何一个,您将很快发现您编写的代码的行为与普通的Perl代码不同.您将无法取代$a<$b 的custom_less_than($a,$b),并有它的工作原理完全相同.
考虑:
> perl -e 'use warnings;
sub my_eq { return ( $_[0]==$_[1] ? 1 : 0 ) }
print "compare (1==0) <<".(1==0).">>"
." to my_eq(1,0) <<".my_eq(1,0).">>"
."\n" ;'
Output:
compare (1==0) <<>> to my_eq(1,0) <<0>>
Run Code Online (Sandbox Code Playgroud)
当您自己编写Perl中的布尔函数时,返回值的最有名的方法是什么?
也许你希望你的代码类似于Perl,可能是Perl现有布尔运算符的替代品.或许您想要数字值,如1/0.或者你可能来自LISP,并且期望Perl undef被用作LISP的nil假(但是你会把Perl的隐含处理许多其他值视为假).
我返回1表示布尔值true.我真的看不到!!1除了混乱之外还有其他任何东西.
但我通常不返回布尔值false.这是因为裸return将返回一个适当的值,具体取决于子程序的调用方式.说明这个的文件return:
如果没有给出EXPR,则返回列表上下文中的空列表,标量上下文中的未定义值,以及(当然)void上下文中什么都没有.
这很重要,因为在标量和列表上下文之间,true和false值可能会略有不同.想象一下像这样的子程序:
sub some_boolean {
if ($some_condition) {
return 1;
else {
return undef; # same effect with any scalar value
}
}
Run Code Online (Sandbox Code Playgroud)
如果在标量上下文中调用它,这可以正常工作.
if (some_boolean()) {
...
} else {
...
}
Run Code Online (Sandbox Code Playgroud)
一切都按预期工作.但如果你在列表上下文中调用它,事情就会变得有些奇怪.
my @array = some_boolean();
if (@array) {
...
} else {
...
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,else永远不会调用该块.在列表上下文中,子例程返回包含单个标量(值undef)的列表,因此@array具有单个元素,并且if (@array)测试始终为true.
当然,您的子例程并不打算在列表上下文中调用.但你无法控制其他程序员如何使用你的代码.
但如果子程序是这样写的:
sub some_boolean {
if ($some_condition) {
return 1;
} else {
return;
}
}
Run Code Online (Sandbox Code Playgroud)
一切都会按预期工作.如果在标量上下文中调用子例程,它将返回标量false值,如果在列表上下文中调用它,它将返回一个空列表.
在我看来,从子例程返回显式的伪标量值始终是可疑的.
如果你想返回一个显式的标量假值,那么检查调用上下文并采取适当的操作是非常值得的.
croak 'Subroutine called in list context.' if wantarray;
Run Code Online (Sandbox Code Playgroud)
更新:回答此评论.
IIRC我尝试使用这种方法,但放弃了它,因为它产生了更多关于未定义值的警告 - 即它不是现有Perl 1/!! 0比较的替代品,也不是1/0.考虑
perl -wle 'print "a()=<<".a().">>\n"; sub a {if(@_) {return 1} else {return}} ',对比return !!@_
布尔值的作用是您可以询问它是真还是假.就这些.在我看来,你不应该期望能够只打印一个布尔值.您经常可以在不触发警告的情况下打印布尔值这一事实很好,但不应该依赖它.如果我想打印一个布尔值,我总是会使用某种逻辑检查(可能是三元运算符)来完成它.所以我会像这样写你的例子:
$ perl -wle 'print "a()=<<".(a() ? "True" : "False").">>\n"; sub a {if(@_) {return 1} else {return}}'
Run Code Online (Sandbox Code Playgroud)
我只想重申我原来的观点.如果您返回false的任何标量值,并且有任何方法可以在列表上下文中调用子例程(甚至意外),那么您已经在代码中引入了潜在的错误.
仅此一点,在打印它们之前必须解码布尔值是非常值得的.
Kra*_*lew -2
---+ 简答
\n\n以下是 Perl 中布尔值的两个自洽方案:
\n\n1/0- 可打印和便携1/!!0- 最像 Perl 的原生布尔函数1/0对于来自其他语言(例如 C 或 Python)的程序员来说可能是最熟悉的。您可以打印1/0布尔值、添加它们等等。但是... Perl 的原生布尔运算符不是1/0值, return $a<0与return 1 if $a<0; return 0.
1/0!!我尝试为 Perl 的本机布尔运算符使用的方案创建一个缩写名称:1 表示 true,这是一个经过特殊标记的空字符串,以便在算术中使用或插入字符串时不会产生警告。!!0是产生这种特殊 Perl false 值的最简单方法之一,并且作为标准化布尔值的一种方法,使用许多语言(如 C)的程序员应该熟悉。您可以添加1/0!!,并且可以打印1/0布尔值,只要您不关心 false 值是否可能不可见(即空字符串)即可。
避免在同一函数或库中意外混合数字 1/0、Perl 的本机条件、以及其他方案。undef
当故意混合时,使用转换运算符,例如
\n\n!!$bool_0or1转换为传统1/!!0Perl 值(0+($a<0))将 Perl 关系结果转换为1/0(!!any_other_boolean())(0+any_other_boolean())($other_bool?1:())转换为1/()从其他布尔方案($other_bool?1:undef)1/undef从其他布尔方案转换为问:除了 之外,还有更短的或带有前缀的符号吗?:?
还有一些可能的方案
\n\n1/()- 更准确地说,返回 1 或什么也不返回- 这可以捕获(1)/(0)- 返回包含 1 的长度为 1 的列表,或一个空\n列表。与 类似1/(),在 true 和 false 都是数组的意义上是一致的。1/undef- 另一种可能性,捕获返回 1 的错误1/() \也没有任何可能导致我犹豫是否要推荐这些。至少,1/()这是不一致的,但是……它们肯定已经被 Perl 程序员使用过,所以您应该准备好处理使用这些方案的代码。即准备好调试由这些方案引起的错误。
1/()我尝试为一个方案创建一个缩写名称,其中返回 true 的函数return 1;和返回 false 的函数 do return;,即返回没有操作数值的函数。即返回 1 或什么也不返回。我相信return Nothing相当于return ();. 此方案可以保护您免受程序员在列表上下文而不是标量上下文中评估您的函数所引起的错误。但它会让您面临诸如{1=>return_nothing_if_false(),2=>return_nothing_if_false()}(因为您可能不想要{1=>2}.
顺便说一句,我认为做这个计划可能会更加一致(1)/()。诚然,这将使您能够一致地拥有这种布尔类型的变量@array变量。
请注意,这1/undef并不等同于上述任何内容。 1/undef布尔变量在 false= 时发出警告undef打印、插值或在算术中使用 false= 时发出警告,但在如此操作 true=1 时则不会发出警告,并且在列表上下文中计算结果为 true。人们可能会想说它具有所有方案中最糟糕的特征。
我犹豫犹豫这些方案1/(),,(1)/()还是1/undef。至少,1/()在这一点上是不一致的
所有这三个方案1/()、(1)/()或 1/undef 都会让您遇到诸如 之类的错误{1=>f1_return_nothing_if_false(),2=>f2_return_nothing_if_false()},因为如果两者都为 false,您可能不希望这样做{a=>"b"},并且{a=>1,b=>1}两者都为真。至少如果 f1 返回 true 并且 f2 返回 false,或者反之亦然,您将收到有关奇数大小的哈希的警告。
调用函数的程序员可以控制它是在列表上下文还是标量上下文中计算,但她可能无法控制它返回 true 还是 false。
\n\n恕我直言,如果您执行 1/()、(1)/()或 1/undef 操作,则无法在数组上下文中安全地调用此类函数,例如构建关键字参数foo(kw=>boolfunc(),...)或foo({kw=>boolfunc(),kw2=>...},...). 不是没有必要分散!或全部为 0+。
---+ 中等长度的答案
\n\n概括原始答案:
\n\nPerl 有多种表示真理的方法;或者更确切地说,Perl 将许多不同的值解释为 true 或 false。
\n\n如果您正在创建一系列相关函数,即。一个库,建议您选择以下众所周知的方案之一,并在您的库中一致使用它:
\n\n真相1/0- 数字 - 与其他语言之间最可移植,并且更易于打印
Truth 1/!!0- 最像标准 Perl 关系运算符,可移植性较差,可打印性较差(除非您希望 false 不可见)
这个答案强调布尔函数或方法、谓词。它并不是试图讨论返回数字、字符串或引用等实际内容的非布尔函数 - 除了下面简要介绍的内容。
\n\n@DaveCross 提出了一个额外有趣的方案
\n\nreturn 1/return什么都没有(几乎1/()是空列表)我记得 Perl 早期的一个方案 - 在 refs 之前,我认为甚至在之前undef也是一个可以返回的值。但是IIRC这个方案存在问题se warnings,u也可能存在问题?:,所以我犹豫是否完全推荐它,直到\xc2\xa0有人更好地解释如何避免此类问题。可能使用wantarray.
---++ 选择 1/0 或 1/0!! (原生 Perl)并保持一致
\n\n我建议您选择这些布尔方案之一,并一致地使用该方案。
\n\n1/0 布尔方案可能最容易移植到其他语言。
\n\n1/!!0 方案将使您的代码更类似于本机 Perl 运算符。
\n\n如果您正在使用该1/!!0方案,请不要说“return 0”,而要说return !!0。
如果您正在使用该1/0方案,请不要说return $a < $b,而是说return 0+($a < $b)
如果您调用的代码使用不同的布尔方案(或者可能没有一致的方案),请使用以下运算符转换为您在代码中使用的布尔方案:
\n\n!!规范化标准 Perl1/0!!布尔值0+或者1*从标准 Perl 1/0 转换为更便携的 1/0 布尔值!!布尔值?:以及 Perl 的所有其他 undef 和字符串(可能或可能不希望被视为错误或失败)如果查看返回 ref 或 undef 的函数的返回值
\n\n如果是1/!!0类似 Perl 的布尔值,请说 return!!ref_retval_func()或defined ref_retval_func()
如果1/0更可移植的布尔值,请说 return0+!!ref_retval_func()或0+(defined ref_retval_func())
下面的细节太多了。
\n\n---++ 可能吗?:返回 1 或不返回任何方案(可能是 1/()?)
\n\n@DaveCross 提出了一个有趣的建议:
\n\n返回 1 表示布尔真值。
对于布尔假值不返回任何内容。这是因为\n简单的
返回将返回一个适当的值,具体取决于
子例程的调用方式。退货单据上是这么说的:
如果没有给出 EXPR,则在列表上下文中返回一个空列表,在标量上下文中返回未定义的值,并且(当然)在 void 上下文中什么也不返回。
---++ 反推荐:不要混合布尔方案\n例如,在同一函数或库中,不要这样做
\n\nreturn $arg1 < $arg2; # returning a standard Perl 1/!!0 Boolean\nRun Code Online (Sandbox Code Playgroud)\n\n在一个地方,然后在其他地方,或者在同一代码的后续演变中,执行以下操作:
\n\nreturn 0; \nreturn undef; \nreturn \'\'; \nreturn (); \nRun Code Online (Sandbox Code Playgroud)\n\n即选择一种布尔方案,并保持一致。主要是,这涉及到错误值的一致性;在较小程度上反映了真实价值。
\n\n---+ 过多混乱的细节
\n\n---++ 在别处讨论 Perl 的许多真理值
\n\n诸如返回布尔值的 Perl 函数实际返回什么以及为什么 Perl 使用空字符串来表示布尔假值?讨论 Perl 布尔函数和运算符实际返回什么。基本上是特殊值,其行为由 Perl 手册指定。
\n\n@cim 链接到 perl 手册:http://perldoc.perl.org/perlsyn.html#Truth-and-Falsehood
\n\n\n\n\n真理与谎言
\n\n数字 0、字符串 \'0\' 和 "" 、空列表 () 和 undef\n 在布尔上下文中都是 false。所有其他值均为 true。\n 通过 ! 对真值求反 or not 返回一个特殊的 false 值。\n 当计算为字符串时,它被视为 "" ,但作为数字时,\n 被视为 0。大多数返回 true 或 false 的 Perl 运算符都以这种方式运行。
\n
同样http://perldoc.perl.org/perlop.html#Relational-Operators
\n\n\n\n\n关系运算符
\n\n返回 true 或 false 的 Perl 运算符通常返回可以安全地用作数字的值。例如,本节中的关系运算符\n 和下一节中的相等运算符返回 1\n(表示 true)和定义的空字符串的特殊版本 "" ,\n 算作零,但不会产生警告关于不正确的数字转换,就像“0 but true”一样。
\n
不幸的是,接受的答案What do Perl Functions that return Boolean 实际上返回\n讨论了内部结构,但随后建议
\n\nmy $formatted = $result ? \'1\' : \'0\';\nRun Code Online (Sandbox Code Playgroud)\n\n这又回到了我们开始的地方。
\n\n@amon 在对返回布尔值的 Perl 函数实际上返回什么的评论中向我们展示了光明(!!)
\n\n\n\n\n旁注:您可以使用双重否定将任何值转换为其相应的布尔值。这导致了!!伪运算符。对于返回通用的真值或假值而不是一些神奇的数字非常有用。\xe2\x80\x93 阿蒙 2012 年 11 月 22 日 22:11
\n
这些特殊布尔值似乎没有任何文字。然而,有很多方法可以生成它们:(0<0)、(0<1)等, (!!1)并且(!!0)可能是最好的 - 特别是因为在某些 C/C++ 编程圈中它们用于类似的目的。另外,!!可以应用于传入的真值,将其“规范化”为“Perl 标准”布尔值。
---++ 反建议:不要混合布尔方案\n例如,在同一函数或库中,不要这样做
\n\nreturn $arg1 < $arg2; # returning a standard Perl 1/!!0 Boolean\nRun Code Online (Sandbox Code Playgroud)\n\n在一个地方,然后在其他地方,或者在同一代码的后续演变中,执行以下操作:
\n\nreturn 0; \nreturn undef; \nreturn \'\'; \nreturn (); \nRun Code Online (Sandbox Code Playgroud)\n\n即选择一种布尔方案,并保持一致。主要是,这涉及到错误值的一致性;在较小程度上反映了真实价值。
\n\n例如,避免从以下位置演变代码
\n\nreturn $arg1 < $arg2; # returning a standard Perl 1/!!0 Boolean\nRun Code Online (Sandbox Code Playgroud)\n\n到
\n\nif( $arg1 < $arg2 ) {\n log_or_print(\'found $arg1 <$arg2\');\n # other stuff to do if less-than\n return 1;\n} else {\n log_or_print(\'found not( $arg1 < $arg2)\');\n # other stuff to do if not-less-than\n # which may not be the same thing as greater-than-or-equal\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n或者
\n\nif( $arg1 < $arg2 ) {\n ...\n} else {\n ...\n return undef;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n从其他地方来到 Perl,您可能会认为它们是等价的,而且它们大多是等价的,但是如果您在测试中执行诸如打印布尔返回值之类的操作,您将得到差异。
\n\n如果您从 Perl 风格的布尔运算符演变代码
\n\nreturn $arg1 < $arg2; # returning a standard Perl 1/!!0 Boolean\nRun Code Online (Sandbox Code Playgroud)\n\n将其演化为
\n\nif( $arg1 < $arg2 ) {\n log_or_print(\'found $arg1 <$arg2\');\n # other stuff to do if less-than\n return 1;\n} else {\n log_or_print(\'found not( $arg1 < $arg2)\');\n # other stuff to do if not-less-than\n # which may not be the same thing as greater-than-or-equal\n return !!0;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n如果您希望行为尽可能接近相同。注意 false 返回值上的 !!0,据我所知,没有更简单的方法来构造 Perl 的 false 特殊返回值。
\n\n相反,如果您想使用 1/0 布尔方案,则原始代码应写为
\n\nreturn 0+($arg1 < $arg2); # returning a standard Perl 1/!!0 Boolean\nRun Code Online (Sandbox Code Playgroud)\n\n---++ 从 value / undef 创建谓词
\n\n同样,您可能会想要采用如下函数
\n\nsub find_string_in_table {\n # returns string value if found, undef if not found\n return $lookup_table->{$_[0]}; \n}\nRun Code Online (Sandbox Code Playgroud)\n\n并将其重构为谓词
\n\nsub is_string_in_table {\n return find_string_in_table(@_);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n然后可能会演变为进行健全性检查或性能优化。
\n\nsub is_string_in_table {\n return 0 \n # don\'t even bother for long strings\n if 1000000 < length($_[0]);\n return find_string_in_table(@_);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这既不是 1/0 也不是 1/!!0,也不是一致的 value/undef。
\n\n(注:我并不是说这个预检查是性能优化——而是说性能优化可能看起来像上面这样。性能优化是我的专长之一,而你希望这样的优化被重构。这很糟糕当优化的代码执行得更好,但在某些使用它的地方出现中断时。因此,我对那些执行得完全相同的代码感兴趣......无论它正在替换什么,就像本机 Perl 关系运算符一样。Exactly 意味着完全一样。)
\n\n如果您使用标准 Perl-ish 布尔值,请执行类似以下操作。
\n\nsub is_string_in_table {\n return !!0 \n # don\'t even bother for long strings\n if 1000000 < length($_[0]);\n return (defined find_string_in_table(@_));\n}\nRun Code Online (Sandbox Code Playgroud)\n\n或者如果您使用 1/0 布尔值
\n\nsub is_string_in_table {\n return 0 \n # don\'t even bother for long strings\n if 1000000 < length($_[0]);\n return 0+(defined find_string_in_table(@_));\n}\nRun Code Online (Sandbox Code Playgroud)\n\n如果不是 find_string_in_table 而是 find_object_ref_in_table,您可能只需返回,0+!!find_string_in_table(@_)因为您不需要担心像q()and 之类的字符串"0"。
如果您希望您编写的代码中的布尔函数的行为类似于本机 Perl 运算符,则返回 (!!1) 表示 true,返回 (!!0) 表示 false。
\n\n即 0/1,但使用 ! 逻辑否定两次 运算符将 1 或 0 转换为 Perl 的“本地”布尔值。
\n\n例如
\n\nsub my_boolean_function {\n ... \n return !!1; # true \n ...\n return !!0; # false\n}\nRun Code Online (Sandbox Code Playgroud)\n\n**----+ 0/1 --> 1/!!0 转换 **
\n\n如果你考虑的话!!作为从“元布尔值”到“特殊布尔值”的转换,
\n\n将 1* 或 0+ 视为从特殊布尔值到普通 0/1 布尔值的转换。
\n\n例如print "test".(1*($a eq $b))."\\n"
例如print "test".(0+($a eq $b))."\\n"
?:\xc2\xa0 更通用,但更详细。
\n\n---++ 非布尔错误返回
\n\n这个问答强调布尔函数或方法、谓词。它并不是试图讨论返回数字、字符串或引用等实际内容的非布尔函数 - 除了下面简要介绍的内容。
\n\n将返回值扩展为指示特殊条件(例如失败、无效输入等)是“很好”的,并且可以在 IF 语句或其他控制流(例如 and 和 or 运算符)的上下文中对其进行评估,通常用于处理此类错误,例如提供默认值。
\n\n我们将把对非布尔函数的讨论限制在这个简短的列表中:
\n\nref/ undef:对于返回典型http://perldoc.perl.org/perlobj.html对象的函数,对祝福哈希或其他类型的引用。返回undef错误、未找到等。
任意值/undef:对于返回任何类型值、标量数字或字符串、标量 ref 的函数,无论是否祝福。
value/undef 在undef不是合法返回值时效果最好,而在 undef 是合法值时可能会出现问题。例如,想象一个返回哈希字段值的访问器函数 $hash->{field} ——该字段可能合法地具有价值{ field => undef },因此返回 undef dfoes 无法区分不存在的字段和存在但具有 undef 值的字段。
GLEW 个人观点:由于我编写的代码经常需要移植到其他语言,因此我不喜欢利用 Perl 特定的技巧,例如0+"0 but true". "0E0"如果您想象在其他一些语言(例如 C 中的函数convert_string_to_float("0E0")或convert_string_to_int("0x0"). 我更喜欢,"0x0"因为它与 x 看起来很特殊,并且0x0是一个整数值,而0E0在某些语言中被解释为浮点数,因此更有可能给出错误。
---++ 可能吗?:返回 1 或不返回任何方案(可能是 1/()?)
\n\n@DaveCross 提出了一个有趣的建议:
\n\n返回 1 表示布尔真值。
对于布尔假值不返回任何内容。这是因为\n简单的
返回将返回一个适当的值,具体取决于
子例程的调用方式。退货单据上是这么说的:
如果没有给出 EXPR,则在列表上下文中返回一个空列表,在标量上下文中返回未定义的值,并且(当然)在 void 上下文中什么也不返回。
这很重要,因为真值和假值在标量和列表上下文之间可能存在细微差别。想象一下这样的子程序:...
\n\n@DaveCross 继续说明,如果在数组上下文中计算布尔函数,则返回空列表以外的任何值将如何导致错误性丢失。甚至@array=(undef)评价为真。
我希望这个计划能够发挥作用。我想我几年前在 Perl 4 或更早版本中使用过它,但后来放弃了它use warnings我想我几年前在 Perl 4 或更早版本中使用过它,但当开始成为要做的事情
据我所知,我也遇到了条件表达式 ?: 与此约定的问题。
\n\n我已经尝试过“返回”;和“返回();”
\n\n考虑
\n\n% perl -wle \'print "a()=<<".a().">>\\n"; sub a {if(@_) {return 1} else {return}}\'\nUse of uninitialized value in concatenation (.) or string at -e line 1.\na()=<<>>\n\n% perl -wle \'print "a()=<<".a().">>\\n"; sub a {if(@_) {return 1} else {return ()}}\'\nUse of uninitialized value in concatenation (.) or string at -e line 1.\na()=<<>>\n\n% perl -wle \'print "a()=<<".a().">>\\n"; sub a { return @_ } \'\na()=<<0>>\n\n% perl -wle \'print "a()=<<".a().">>\\n"; sub a { return !!@_ } \'\na()=<<>>\n\n%\nRun Code Online (Sandbox Code Playgroud)\n\n---+ 底线
\n\n使用1/0(可打印和便携式),1/0!!(最像 Perl 的本机布尔函数)。
可能return 1或return没有,这几乎与1/()。(但我对这种方法遇到了问题。)
避免混合数字 1/0,Perl 的本机条件,undef避免在同一函数或库中
最后,如果你曾经做过
\n\n$> perl -wle \'print false && true\'\nRun Code Online (Sandbox Code Playgroud)\n\n您可能已经收到
\n\nUnquoted string "false" may clash with future reserved word at -e line 1.\nUnquoted string "true" may clash with future reserved word at -e line 1.\nBareword found in conditional at -e line 1.\ntrue\nRun Code Online (Sandbox Code Playgroud)\n\n所以看来有一天 Perl 可能会有一个布尔值的“官方”方案,其值为 true 和 false。
\n\n我想知道它们会如何表现?
\n| 归档时间: |
|
| 查看次数: |
6392 次 |
| 最近记录: |