Perl中的布尔函数应该返回什么值? [英] What values should a boolean function in Perl return?

查看:159
本文介绍了Perl中的布尔函数应该返回什么值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简短问题

在Perl代码库中始终如一地表示对与错的最佳方法是什么?

  • 1/0?

  • 1/Perl的本机布尔运算符的特殊空字符串
    返回吗?

  • undef?

  • ()(即空列表)?

问题背景

我们都知道,就像大多数事情一样,Perl在布尔值方面非常灵活.

例如,Perl将以下内容视为假:undef(),数字0(即使写为000或0.0),空字符串,'0'(包含单个0位数字的字符串). Perl将以下内容视为true:任何其他标量值,1-1,其中包含空格的字符串(' '),字符串中的'00'多个0,"0\n"('0'后跟换行符),'true''false''undef'等.此外,数组到标量和列表到标量的转换意味着您通常可以避免使用空数组作为假值. (贷方 http://perlmaven.com/boolean-values-in-perl 表示Perl接受为true或false的部分标量值.)

但是考虑到所有这些值都被视为true或false,Perl布尔函数应该返回什么?

  • 1/0
  • -1/0(nah)
  • 1/undef
  • 1/""

如果使用这些约定中的任何一个,您将很快发现所编写的代码的行为不如正常的Perl代码好.您将无法用custom_less_than($a,$b)替换$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>>

您自己编写时从Perl中的布尔函数返回值的最著名的方法是什么?

也许您希望您的代码类似于Perl,可以替换为Perl现有的布尔运算符.或者,也许您想要1/0之类的数值.也许您来自LISP,并希望将Perl的undef用作错误的LISP的nil(但随后您会误以为Perl会将许多其他值当作false隐式对待).

解决方案

--- +简短答案

以下是Perl中布尔值的两个自洽方案:

  • 1/0-可打印且便携式
  • 1/!!0-最像Perl的本机布尔函数

1/0可能是其他语言(例如C或Python)的程序员最熟悉的.您可以打印1/0布尔值,添加它们,依此类推.但是... Perl的本机布尔运算符不是1/0值,return $a<0return 1 if $a<0; return 0不同.

1/0!!是我尝试为Perl的本机布尔运算符使用的方案创建一个缩写名称:1表示true,这是一个特殊标记的空字符串,因此在用于算术或插入到字符串中时不会产生警告. !!0是产生此特殊Perl假值的最简单方法之一,对于像C这样的许多语言的程序员,应该标准化标准布尔值.您可以添加1/0!!,也可以打印1/0布尔值,只要您不关心该假值是否不可见(即空字符串)即可.

避免意外地将数字1/0,Perl的原始条件,undef和其他方案混合在同一函数或库中.

故意混合时,请使用诸如

之类的转换运算符
  • !!$bool_0or1转换为传统的1/!!0 Perl值
  • (0+($a<0))将Perl关系结果转换为1/0
  • (!!any_other_boolean())
  • (0+any_other_boolean())
  • ($other_bool?1:())可以从其他布尔方案转换为1/()
  • ($other_bool?1:undef)可以从其他布尔方案转换为1/undef

问:除了?:之外,是否还有其他简短或带有前缀的符号?

还有更多可能的方案

  • 1/()-更准确地说,返回1或不返回-可以捕获
    一些Perl-ish错误,例如返回布尔标量false,例如0或 在列表上下文中取消定义,在这种情况下将变为真
  • (1)/(0)-返回包含1的长度为1的列表,或者为空 列表.与1/()相似,在某种意义上是一致的 数组是假的.
  • 1/undef-另一种可能性,捕获到1/() 返回1的错误 或什么都不会引起

我毫不犹豫地推荐这些.至少,1/()的不一致之处在于……但是……它们肯定已被Perl程序员使用,因此您应该准备好处理使用这些方案的代码. IE.准备调试由这些方案引起的错误.

1/()是我为方案的缩写名称的尝试,其中返回true的函数return 1;和返回false的函数执行return;,即没有操作数值的返回. IE. 返回1或不返回.我相信什么也不返回等同于return ();.这种方案可以防止程序员在列表上下文而不是标量上下文中评估函数而导致的错误.但这会使您面临诸如{1=>return_nothing_if_false(),2=>return_nothing_if_false()}的错误(因为您可能不希望{1=>2}.

顺便说一句,我 认为执行方案(1)/()可能更一致.这样一来,您就可以保持一致;您可以拥有布尔类型的变量,当然可以是@array变量.

请注意,1/undef不等同于上述任何内容. 1/undef布尔变量在打印或插入或在算术中使用false = undef时发出警告,但在如此操作true = 1时不会发出警告,并且在列表上下文中计算为true.有人可能会说它具有所有方案中最糟糕的功能.

我很犹豫这些方案1/()(1)/()或1/undef.至少1/()

不一致

这三个方案1/()(1)/()或1/undef都会使您暴露于诸如{1=>f1_return_nothing_if_false(),2=>f2_return_nothing_if_false()}的错误,因为您可能不希望{a=>"b"}(如果都为false)和{a=>1,b=>1}(如果都为false)是真的.至少如果f1返回true,f2返回false,反之亦然,您将收到有关奇数大小哈希的警告.

调用函数的程序员可以控制是否在列表或标量上下文中求值,但她可能无法控制其返回true还是false.

恕我直言,如果您执行1/()(1)/()或1/undef,则无法安全地在数组上下文中调用此类函数,例如构建关键字参数foo(kw=>boolfunc(),...)foo({kw=>boolfunc(),kw2=>...},...).并非没有分散!或整个0 +.

--- +中长答案

概括原始答案:

Perl有许多表示真相的方法;或者说,Perl将许多不同的值解释为true或false.

如果要创建一系列相关功能,即.一个库,建议您选择以下知名方案之一,并在您的库中始终使用它:

  1. 真相1/0-数字-与其他语言之间的可移植性最强,并且更具打印性

  2. 真相1/!!0-最像标准Perl关系运算符,可移植性较小,可打印性较低(除非您希望false不可见)

此答案强调布尔函数或方法,谓词.它不在试图讨论非布尔函数,该函数返回诸如数字,字符串或refs之类的实际东西-除了下面的简要介绍.

@DaveCross提出了另一种有趣的方案

  1. return 1/return没有任何内容(几乎是1/(),空列表)

我记得Perl早期的

方案-在引用之前,我认为甚至在undef之前都是可以返回的值.但是IIRC的方案和u se warnings(可能还有?:)都存在问题,因此我犹豫要完全推荐它,直到有人更好地解释了如何避免此类问题为止.可能使用wantarray.

--- ++选择1/0或1/0! (本地Perl)并保持一致

我建议您选择以下布尔方案之一,并始终使用该方案.

1/0布尔方案可能是最易于移植到其他语言的.

1/!! 0方案将使您的代码更类似于本机Perl运算符.

如果使用的是1/!!0方案,请不要说返回0",而要说return !!0.

如果使用的是1/0方案,请不要说return $a < $b,而要说return 0+($a < $b)

如果您要调用使用其他布尔方案(或者可能没有一致方案)的代码,请使用

等运算符将其转换为您在代码中使用的布尔方案.
  • !!标准化标准Perl 1/0!!布尔
  • 0+1*可以从标准Perl 1/0转换为更可移植的1/0布尔值!布尔值
  • ?:以及Perl的所有其他工具库中的undef和字符串,这些undef和字符串可能会或可能不会被视为错误或失败

如果查看返回ref或undef的函数的返回值

  • 如果1/!!0类似Perl的布尔值,则说返回!!ref_retval_func()defined ref_retval_func()

  • 如果1/0更多可移植的布尔值,请说返回0+!!ref_retval_func()0+(defined ref_retval_func())

下面的细节太多了.

--- ++可能吗?:返回1或不返回任何方案(可能是1/()?)

@DaveCross提出了一个有趣的建议:

  • 返回1以获取布尔值.

  • 不为布尔假值返回任何内容.那是因为光秃秃的
    return将返回适当的值,具体取决于
    子程序已被调用.退货文件说明:

    如果未给出EXPR,则在列表上下文中返回一个空列表,在标量上下文中返回未定义的值,在void上下文中(当然)完全不返回任何值.

--- ++反建议:请勿混用布尔方案 例如,在相同的函数或库中,请勿执行

return $arg1 < $arg2;  # returning a standard Perl 1/!!0 Boolean

在一个地方,然后在其他地方,或者在同一代码的后续版本中,做

return 0; 
return undef; 
return ''; 
return (); 

即选择一种布尔方案,并保持一致.主要地,这涉及到关于虚假值的一致性.在较小程度上是真实值.

--- +乱七八糟的细节

--- ++在其他地方讨论Perl的许多真理值

诸如之类的帖子实际上返回布尔值的Perl函数是什么返回为什么Perl使用空字符串表示布尔假值?讨论Perl布尔函数和运算符实际返回的内容.基本上是特殊的值,其行为由Perl手册指定.

@cim链接到perl手册: http://perldoc.perl .org/perlsyn.html#真相与虚伪

真相与虚伪

数字0,字符串'0'和",空列表()和undef 在布尔上下文中都为假.所有其他值都是正确的. 否定真值!或不返回特殊的false值. 以字符串形式求值时,将被视为",但将其视为数字时,将被视为数字 被视为0.大多数返回true或false的Perl运算符的行为 这样.

类似地 http://perldoc.perl.org/perlop.html#Relational-运算符

关系运算符

返回true或false的Perl运算符通常返回以下值: 可以安全地用作数字.例如,关系运算符 在本节中,下一个等式运算符返回1 为true和定义的空字符串"的特殊版本,其中 视为零,但可以免除有关不正确数字的警告 转换,就像"0但为真"一样.

不幸的是,对于 Perl可以做什么的公认答案返回布尔值的函数实际上会返回 讨论内部原理,然后推荐

my $formatted = $result ? '1' : '0';

回到我们开始的地方.

@amon在对问题

--- ++反建议:请勿混用布尔方案 例如,在相同的函数或库中,请勿执行

return $arg1 < $arg2;  # returning a standard Perl 1/!!0 Boolean

在一个地方,然后在其他地方,或者在同一代码的后续版本中,做

return 0; 
return undef; 
return ''; 
return (); 

即选择一种布尔方案,并保持一致.主要地,这涉及到关于虚假值的一致性.在较小程度上是真实值.

例如避免将代码从演变而来

return $arg1 < $arg2;  # returning a standard Perl 1/!!0 Boolean

if( $arg1 < $arg2 ) {
     log_or_print('found $arg1 <$arg2');
     # other stuff to do if less-than
     return 1;
} else {
     log_or_print('found not( $arg1 < $arg2)');
     # other stuff to do if not-less-than
     # which may not be the same thing as greater-than-or-equal
     return 0;
}

if( $arg1 < $arg2 ) {
     ...
} else {
     ...
     return undef;
}

从其他地方来到Perl时,您可能会认为它们是等效的,并且大多数情况下都是等效的.但是,如果您执行诸如在测试中打印布尔返回值之类的事情,则会有所不同.

如果您从Perl布尔运算符演化代码

return $arg1 < $arg2;  # returning a standard Perl 1/!!0 Boolean

将其演变为

if( $arg1 < $arg2 ) {
     log_or_print('found $arg1 <$arg2');
     # other stuff to do if less-than
     return 1;
} else {
     log_or_print('found not( $arg1 < $arg2)');
     # other stuff to do if not-less-than
     # which may not be the same thing as greater-than-or-equal
     return !!0;
}

如果您希望行为尽可能接近相同.请注意,返回false时的!! 0,据我所知,没有更简单的方法来构造Perl的false特殊返回值.

相反,如果要使用1/0布尔方案,则它们的原始代码应写为

return 0+($arg1 < $arg2);  # returning a standard Perl 1/!!0 Boolean

--- ++从值/undef创建谓词

类似地,您可能会想使用

之类的功能.

sub find_string_in_table {
   # returns string value if found, undef if not found
   return $lookup_table->{$_[0]}; 
}

并将其重构为谓词

sub is_string_in_table {
   return find_string_in_table(@_);
}

,然后演变为进行健全性检查或性能优化.

sub is_string_in_table {
   return 0 
       # don't even bother for long strings
       if 1000000 < length($_[0]);
   return find_string_in_table(@_);
}

这既不是1/0也不是1/!! 0,也不是始终不变的value/undef.

(注意:我并不是说这种预检查是一种性能优化---但我说的是性能优化可能看起来像上面的一样.性能优化是我的专长之一,您希望对这种优化进行重构. .当优化的代码执行得更好时,它很烂,但是在某些地方却被打破了.因此,我对代码的性能表现出与...一样的兴趣,就像正在替换的东西一样,例如本机Perl关系运算符.确切地意味着.)/p>

如果您使用的是标准的Perl布尔值,请执行以下操作.

sub is_string_in_table {
   return !!0 
       # don't even bother for long strings
       if 1000000 < length($_[0]);
   return (defined find_string_in_table(@_));
}

或者如果您使用的是1/0布尔值

sub is_string_in_table {
   return 0 
       # don't even bother for long strings
       if 1000000 < length($_[0]);
   return 0+(defined find_string_in_table(@_));
}

如果不是find_string_in_table而是find_object_ref_in_table,则可能只是返回0+!!find_string_in_table(@_),因为您不必担心q()"0"之类的字符串.

如果要在代码中使用布尔函数,则编写的行为应类似于本机Perl运算符,则返回(!! 1)为true,(!! 0)为false.

即0/1,但在逻辑上使用!取反两次运算符,将您的1或0转换为Perl的本地"布尔值.

例如

sub my_boolean_function {
      ... 
      return !!1; # true 
      ...
      return !!0; # false
}

** --- + 0/1-> 1/!! 0转换**

如果您考虑!从元布尔"到特殊布尔"的转换,

将1 *或0+视为从特殊布尔值到普通0/1布尔值的转换.

例如print "test".(1*($a eq $b))."\n"

例如print "test".(0+($a eq $b))."\n"

?:更笼统,但更冗长.

--- ++非布尔错误返回

此问题和答案着重于布尔函数或方法,谓词.它不在试图讨论非布尔函数,该函数返回诸如数字,字符串或refs之类的实际东西-除了下面的简要介绍.

扩展返回值以指示特殊条件(例如失败,无效输入等)是很好"的做法,并且可以在IF语句或其他控制流(例如和和或或运算符)的上下文中对其进行评估,通常用于处理此类错误,例如提供默认值.

我们将对非布尔函数的讨论限于此简短列表:

undef不是合法的返回值时,

value/undef效果最好,而当undef是合法的值时,可能会出现问题.假设有一个访问器函数返回一个哈希字段的值$ hash-> {field} -该字段可能合法地具有值{ field => undef },那么返回undef dfoes不会区分不存在的字段和存在但存在的字段一个undef值.

GLEW个人意见:由于我编写的代码经常需要移植到其他语言,因此我不希望利用诸如0+"0 but true"之类的Perl特有技巧.如果您设想使用c之类的其他语言编写convert_string_to_float("0E0")convert_string_to_int("0x0")函数,则"0E0"至少更具可移植性.我更喜欢"0x0",因为它与x看起来很特殊,并且0x0是整数值,而0E0在某些语言中被解释为浮点数,因此更可能出现错误.

--- ++可能吗?:返回1或不返回任何方案(可能是1/()?)

@DaveCross提出了一个有趣的建议:

  • 返回1以获取布尔值.

  • 不为布尔假值返回任何内容.那是因为光秃秃的
    return将返回适当的值,具体取决于
    子程序已被调用.退货文件说明:

    如果未给出EXPR,则在列表上下文中返回一个空列表,在标量上下文中返回未定义的值,在void上下文中(当然)完全不返回任何值.

这很重要,因为标量和列表上下文之间的true和false值可能会有所不同.想象这样的子例程:...

@DaveCross继续说明,如果在数组上下文中评估布尔函数,那么返回空列表以外的任何其他值将如何导致虚假性损失.甚至@array=(undef)都认为是正确的.

我希望这个方案能奏效.我想我几年前(在Perl 4或更早版本中)就使用了它,但是当use warnings开始成为要做的事情时就放弃了.

据我所知,我也有条件条件方面的问题吗?:让这个惯例成为现实.

我都尝试过两次返回";和"return();"

考虑

%  perl -wle 'print "a()=<<".a().">>\n"; sub a {if(@_) {return 1} else {return}}'
Use of uninitialized value in concatenation (.) or string at -e line 1.
a()=<<>>

%  perl -wle 'print "a()=<<".a().">>\n"; sub a {if(@_) {return 1} else {return ()}}'
Use of uninitialized value in concatenation (.) or string at -e line 1.
a()=<<>>

%  perl -wle 'print "a()=<<".a().">>\n"; sub a { return @_ } '
a()=<<0>>

%  perl -wle 'print "a()=<<".a().">>\n"; sub a { return !!@_ } '
a()=<<>>

%

--- +底行

使用1/0(可打印和便携式),1/0!!(最类似于Perl的本机布尔函数).

可能return 1return什么都没有,这与1/()几乎相同. (但是我在使用这种方法时遇到了问题.)

避免在同一函数或库中混合使用数字1/0,Perl的原始条件,undef和其他方案.

最后,如果您曾经做过

$>  perl -wle 'print false && true'

您可能已经收到

Unquoted string "false" may clash with future reserved word at -e line 1.
Unquoted string "true" may clash with future reserved word at -e line 1.
Bareword found in conditional at -e line 1.
true

因此,似乎某天Perl可能会使用布尔值的正式"方案,其值为true和false.

我想知道它们会如何表现吗?

SHORT QUESTION

What are the best ways to represent true and false consistently in libraries of Perl code?

  • 1 / 0?

  • 1 / the special empty string that Perl's native boolean operators
    return?

  • undef?

  • () (i.e. the empty list)?

QUESTION BACKGROUND

We all know that Perl is very flexible with regard to booleans, as with most things.

For example, Perl treats the following as false: undef(), the number 0 (even if written as 000 or 0.0), an empty string, '0' (a string containing a single 0 digit). Perl treats the following as true: any other scalar values, 1, -1, a string with a space in it (' '), '00' multiple 0s in a string, "0\n" (a '0' followed by a newline), 'true', 'false', 'undef', etc. Plus, array-to-scalar and list-to-scalar conversions mean that you can often get away with using an empty array as a false value. (Credit to http://perlmaven.com/boolean-values-in-perl for the partial list of scalar values accepted as true or false by Perl.)

But given all of these values that are treated as true or false, what should a Perl boolean function return?

  • 1/0
  • -1/0 (nah)
  • 1/undef
  • 1/""

If you use any of these conventions, you will quickly find that the code that you write does not behave as nicely as normal Perl code. You will not be able to replace $a<$b by custom_less_than($a,$b) and have it work exactly the same.

Consider:

> 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>>

What is the best known method for returning values from boolean functions in Perl when you are writing them yourself?

Perhaps you want your code to be Perl-like, potentially substitutable for Perl's existing boolean operators. Or perhaps you want numeric values like 1/0. Or perhaps you come from LISP, and expect Perl's undef to be used LISP's nil for false (but then you trip upon Perl's implicit treatment of many other values as false).

解决方案

---+ SHORT ANSWER

Here are two self-consistent schemes for Booleans in Perl:

  • 1/0 - printable and portable
  • 1/!!0 - most like Perl's native boolean functions

1/0is probably most familiar to programmers from other languages, like C or Python. You can print 1/0 Boolean values, add them, and so on. But... Perl's native Boolean operators are not 1/0 values, return $a<0 is NOT the same as return 1 if $a<0; return 0.

1/0!! is my attempt to create an abbreviated name for the scheme that Perl's native Boolean operators use: 1 for true, an empty string that is specially marked so that it does not produce warnings when used in arithmetic or interpolated into strings. !!0 is one of the easiest ways to produce this special Perl false value, and should be familiar to programmers in many languages like C as a way of standardizing Boolean values. You can add 1/0!!, and you can print 1/0 Boolean values, so long as you don't care if the false value may be invisible, i.e. an empty string.

Avoid accidentally mixing numeric 1/0, Perl's native conditions, undef, and other schemes in the same function or library.

When deliberately mixing, use conversion operators such as

  • !!$bool_0or1 to convert to a traditional 1/!!0Perl value
  • (0+($a<0)) to convert a Perl relational result to 1/0
  • (!!any_other_boolean())
  • (0+any_other_boolean())
  • ($other_bool?1:()) to convert to 1/() from the other boolean schemes
  • ($other_bool?1:undef) to convert to 1/undef from the other boolean schemes

Q: are there any shorter or prefix-ish notations, other than ?:?

There are some more possible schemes

  • 1/() - more precisely, return 1 or nothing -- which can catch
    some Perl-ish errors like returning a boolean scalar false like 0 or undef in a list context, where it would become true
  • (1)/(0) - return a list of length 1 conrtaining 1, or an empty list. Similar to 1/(), consistent in the sense that both true and false are arrays.
  • 1/undef - another possibility, catches errors that 1/() return 1 or nothing may cause

I hesitate to recommend these. At the very least, 1/() is inconsistent in that But... they certainly have been used by Perl programmers, so you should be prepared to deal with code that uses these schemes. I.e. be prepared to debug bugs caused by these schemes.

1/() is my attempt to create an abbreviated name for a scheme where functions that return true return 1;, and functions that return false do return;, i.e. a return with no operand value. I.e. return 1 or nothing. I believe that return nothing is equivalent to return ();. This scheme protects you against bugs caused by a programmer evaluating your function in a list context rather than a scalar context. But it exposes you to bugs such as {1=>return_nothing_if_false(),2=>return_nothing_if_false()} (snce you probably do not want {1=>2}.

BTW I think that it might be more consistent to do the scheme (1)/(). This would allow you to consistentl;y have variables of this Boolean type, admittedly @array variables.

Note that 1/undef is NOT equivalent to any of the above. 1/undef Boolean variables give warnings when false=undef is printed or interpolated or used in arithmetic, but not when true=1 is so-manipulated, and evaluates to true in a list context. One might be tempted to say that it has the worst features of all the schemes.

I hesitate to hesitate these schemes 1/(), (1)/(), or 1/undef. At the very least, 1/() is inconsistent in that

All three of these schemes 1/(), (1)/(), or 1/undef expose you to bugs such as {1=>f1_return_nothing_if_false(),2=>f2_return_nothing_if_false()} , since you probably do not want {a=>"b"} if both are false, and {a=>1,b=>1} if both are true. At least if f1 returns true and f2 returns false, or vice versa, you will get a warning about odd sized hash.

The programmer calling a function can control whether it is evaluated in list or scalar context, but she may not be able to control whether it returns true or false.

IMHO, if you do 1/(), (1)/(), or 1/undef you cannot safely call such functions in array context, like building keyword arguments foo(kw=>boolfunc(),...) or foo({kw=>boolfunc(),kw2=>...},...). Not without having to scatter !! or 0+ all over.

---+ MEDIUM LENGTH ANSWER

Generalizing original answer:

Perl has many ways of representing truth; or, rather, Perl interprets many different values as true or false.

If you are creating a family of related functions, i.e,. a library, you are advised choose one of the following well-known schemes, and to use it consistently in your library:

  1. Truth 1/0 - numeric - most portable to/from other languages, and more printable

  2. Truth 1/!!0 - most like standard Perl relational operators, less portable, less printable (unless you want false to be invisible)

This answer emphasizes Boolean functions or methods, predicates. It is not trying to discuss non-Boolean functions, that return actual things like numbers or strings or refs - except briefly below.

@DaveCross suggests an additional interesting scheme

  1. return 1 / return nothing (almost 1/(), the empty list)

a scheme that I remember from the early days of Perl - before refs, I think even before undef was a value that could be returned. But IIRC there are problems with this scheme and use warnings, possibly also ?:, so I hesitate to recommend it fully until somebody explains better how to avoid such problems. Possibly using wantarray.

---++ Choose 1/0 or 1/0!! (native Perl) and be consistent

I recommend that you choose one of these Boolean schemes, and use that scheme consistently.

The 1/0 boolean scheme is probably most portable to other languages.

The 1/!!0 scheme will make your code more closely resemble native Perl operators.

If you are using the 1/!!0 scheme, don't say "return 0", say return !!0.

If you are using the 1/0 scheme, don't say return $a < $b, instead say return 0+($a < $b)

If you are calling code that uses a different Boolean scheme (or, possibly, no consistent scheme), convert to the Boolean scheme that you use in your code, using operators such as

  • !! to normalize a standard Perl 1/0!! Boolean
  • 0+ or 1* to convert to a more portable 1/0 boolean from a standard Perl 1/0!! boolean
  • ?: and all the rest of Perl's arsenal for undefs and strings that may or may not want to be considered false or failures

If looking at the return value of a function that returns a ref or undef

  • If 1/!!0 Perl-like Booleans, say return !!ref_retval_func() or defined ref_retval_func()

  • If 1/0 more portable Booleans, say return 0+!!ref_retval_func() or 0+(defined ref_retval_func())

Way too much detail below.

---++ Possible?: return 1 or return nothing scheme (possibly 1/()?)

@DaveCross makes an interesting suggestion:

  • return 1 for a Boolean true value.

  • return nothing for a Boolean false value. That's because a bare
    return will return an appropriate value depending on how the
    subroutine has been called. The documentation for return says this:

    If no EXPR is given, returns an empty list in list context, the undefined value in scalar context, and (of course) nothing at all in void context.

---++ Anti-Recommendation: do not mix Boolean schemes E.g., in the same function or library, do NOT do

return $arg1 < $arg2;  # returning a standard Perl 1/!!0 Boolean

in one place, and then somewhere else, or in later evolutions of the same code, do

return 0; 
return undef; 
return ''; 
return (); 

I.e. choose one Boolean scheme, and be consistent. Mainly, this involves being consistent about the false value; to a lesser extent the true value.

---+ EXCESSIVE MESSY DETAIL

---++ Discussion elsewhere about Perl's many values of truth

Posts such as What do Perl functions that return Boolean actually return and Why does Perl use the empty string to represent the boolean false value? discuss what Perl boolean functions and operators actually return. Basically special values, whose behavior is specified by the Perl manuals.

@cim links to the perl manuals: http://perldoc.perl.org/perlsyn.html#Truth-and-Falsehood

Truth and Falsehood

The number 0, the strings '0' and "" , the empty list () , and undef are all false in a boolean context. All other values are true. Negation of a true value by ! or not returns a special false value. When evaluated as a string it is treated as "" , but as a number, it is treated as 0. Most Perl operators that return true or false behave this way.

Similarly http://perldoc.perl.org/perlop.html#Relational-Operators

Relational Operators

Perl operators that return true or false generally return values that can be safely used as numbers. For example, the relational operators in this section and the equality operators in the next one return 1 for true and a special version of the defined empty string, "" , which counts as a zero but is exempt from warnings about improper numeric conversions, just as "0 but true" is.

Unfortunately, the accepted answer for What do Perl functions that return Boolean actually return discusses the internals, but then recommends

my $formatted = $result ? '1' : '0';

which is back to where we started.

@amon shows us the light (!!) in a comment on the question What do Perl functions that return Boolean actually return

Sidenote: you can turn any value into its corresponding boolean with double negation. This leads to the !! pseudo-operator. Very useful for returning the generic truthy or falsey value instead of some magic number. – amon Nov 22 '12 at 22:11

There does not seem to be any literal for these special Booleans. There are, however, many ways of producing them: (0<0), (0<1), etc. (!!1) and (!!0) are probably the nicest - especially since in some C/C++ programming circles they are used for a similar purpose. Plus, !! can be applied to an incoming truth value to "normalize" it to this "Perl standard" boolean.

---++ Anti-Recommendation: do not mix Boolean schemes E.g., in the same function or library, do NOT do

return $arg1 < $arg2;  # returning a standard Perl 1/!!0 Boolean

in one place, and then somewhere else, or in later evolutions of the same code, do

return 0; 
return undef; 
return ''; 
return (); 

I.e. choose one Boolean scheme, and be consistent. Mainly, this involves being consistent about the false value; to a lesser extent the true value.

e.g. Avoid evolving code from

return $arg1 < $arg2;  # returning a standard Perl 1/!!0 Boolean

to

if( $arg1 < $arg2 ) {
     log_or_print('found $arg1 <$arg2');
     # other stuff to do if less-than
     return 1;
} else {
     log_or_print('found not( $arg1 < $arg2)');
     # other stuff to do if not-less-than
     # which may not be the same thing as greater-than-or-equal
     return 0;
}

or

if( $arg1 < $arg2 ) {
     ...
} else {
     ...
     return undef;
}

Coming to Perl from somewhere else, you may think that these are equivalent, and they mostly are, but if you do things like printing boolean return values in tests you will get differences.

If you evolve code from a Perl-ish boolean operator

return $arg1 < $arg2;  # returning a standard Perl 1/!!0 Boolean

evolve it to

if( $arg1 < $arg2 ) {
     log_or_print('found $arg1 <$arg2');
     # other stuff to do if less-than
     return 1;
} else {
     log_or_print('found not( $arg1 < $arg2)');
     # other stuff to do if not-less-than
     # which may not be the same thing as greater-than-or-equal
     return !!0;
}

If you want behavior to be as close to the same. Note the !!0 on the return of false, As far as I know, there is no simpler way to construct Perl's special return value for false.

Conversely, if you want to use the 1/0 Boolean scheme, them the original code should have been written as

return 0+($arg1 < $arg2);  # returning a standard Perl 1/!!0 Boolean

---++ Creating a predicate from value / undef

Similarly, you may be tempted to take a function such as

sub find_string_in_table {
   # returns string value if found, undef if not found
   return $lookup_table->{$_[0]}; 
}

and refactor it to a predicate

sub is_string_in_table {
   return find_string_in_table(@_);
}

and then later evolve to, perhaps, have a sanity check or performance optimization.

sub is_string_in_table {
   return 0 
       # don't even bother for long strings
       if 1000000 < length($_[0]);
   return find_string_in_table(@_);
}

This is neither 1/0 nor 1/!!0, and is not consistently value/undef either.

(Note: I am not saying this pre-check is a performance optimization --- but I am saying that performance optimizations might look like the above. Performance optimization is one of my specialties, and you want such optimizations to be refactoring. It sucks when optimized code performs better, but breaks in some of the places it is used. Hence my interest in code that performs as exactly like ... whatever it is replacing, like native Perl relational operators. Exactly means exactly.)

Instead do something like the following if you are using standard Perl-ish booleans.

sub is_string_in_table {
   return !!0 
       # don't even bother for long strings
       if 1000000 < length($_[0]);
   return (defined find_string_in_table(@_));
}

or if you are using 1/0 Booleans

sub is_string_in_table {
   return 0 
       # don't even bother for long strings
       if 1000000 < length($_[0]);
   return 0+(defined find_string_in_table(@_));
}

If instead of find_string_in_table it was find_object_ref_in_table, you might do just return 0+!!find_string_in_table(@_) because you would not need to worry about strings like q() and "0".

If you want boolean functions in code you write to behave like native Perl operators return (!!1) for true and (!!0) for false.

I.e. 0/1, but logically negate twice using the ! operator to convert your 1 or 0 into Perl's 'native' boolean.

e.g.

sub my_boolean_function {
      ... 
      return !!1; # true 
      ...
      return !!0; # false
}

**---+ 0/1 --> 1/!!0 conversion **

If you consider !! as a conversion from "meta-boolean" to "special boolean",

consider 1* or 0+ as a conversion from special boolean to ordinary 0/1 boolean.

E.g. print "test".(1*($a eq $b))."\n"

E.g. print "test".(0+($a eq $b))."\n"

?: is more general, but more verbose.

---++ Non-Boolean error returns

This question and answer emphasizes Boolean functions or methods, predicates. It is not trying to discuss non-Boolean functions, that return actual things like numbers or strings or refs - except briefly below.

It is "nice" to have the return value extended to indicate special conditions such as failure, invalid input, etc., and which may be evaluated in the context of IF statements or other control flow such as and and or operators, typically to handle such errors, e.g. to provide default values.

We will limit our discussion of non-Boolean functions to this short list:

  • ref / undef : for functions that return a typical http://perldoc.perl.org/perlobj.html object, a ref to a blessed hash or other type. Return undef on error, not found, etc.

  • any value / undef : for functions that return any type of value, scalar number or string, scalar ref whether blessed or unblessed.

value/undef works best when undef is not a legitimate return value and can be problematic when undef is a legitimate value.n E.g. imagine an accessor function that returns the value of a hash field, $hash->{field} -- the field might legitimately have the value { field => undef }, so returning undef dfoes not distinguish between the field not existing and the field existing but having an undef value.

  • Arbitrary strings, that may contextually be interpreted as numbers or booleans.
  • "0 but true" - I really don't want to get into this, but look at What does "0 but true" mean in Perl? for the special handling of string "0 but true". Other strings give warnings on conversion to a number, but "0 but true" does not.
  • "0E0" - Similarly, some Perl code returns the string "0E0" which evaluates to 0 as a number, but true as a Boolean

GLEW personal opinion: since I write code that often needs to be ported to other languages, I prefer not to take advantage of Perl-specific tricks like 0+"0 but true". "0E0" at least is more portable, if you imagine that in some other language like C a function convert_string_to_float("0E0") or convert_string_to_int("0x0"). I prefer "0x0" because it looks special with the x, and 0x0 is an integer value, whereas 0E0 is interpreted as a float in some languages, so is more likely to give an error.

---++ Possible?: return 1 or return nothing scheme (possibly 1/()?)

@DaveCross makes an interesting suggestion:

  • return 1 for a Boolean true value.

  • return nothing for a Boolean false value. That's because a bare
    return will return an appropriate value depending on how the
    subroutine has been called. The documentation for return says this:

    If no EXPR is given, returns an empty list in list context, the undefined value in scalar context, and (of course) nothing at all in void context.

This is important as true and false values can differ subtly between scalar and list context. Imagine a subroutine like this: ...

@DaveCross goes on to show how returning any value other than the empty list results in loss of false-ness if a boolean function is evaluated in array context. Even @array=(undef) evaluates as true.

I would like this scheme to work. I think that I used it years ago, in Perl 4 or earlier, but gave up on it when use warnings started becoming the thing to do.

Insofar as I recall, I also had problems with conditional exprtessions ?: wigth this convention.

I have tried both "return;" and "return();"

Consider

%  perl -wle 'print "a()=<<".a().">>\n"; sub a {if(@_) {return 1} else {return}}'
Use of uninitialized value in concatenation (.) or string at -e line 1.
a()=<<>>

%  perl -wle 'print "a()=<<".a().">>\n"; sub a {if(@_) {return 1} else {return ()}}'
Use of uninitialized value in concatenation (.) or string at -e line 1.
a()=<<>>

%  perl -wle 'print "a()=<<".a().">>\n"; sub a { return @_ } '
a()=<<0>>

%  perl -wle 'print "a()=<<".a().">>\n"; sub a { return !!@_ } '
a()=<<>>

%

---+ BOTTOM LINE

Use 1/0 (printable and portable), 1/0!! (most like Perl's native boolean functions).

Possibly return 1 or return nothing, which is almost the same as 1/(). (But I have had problems with this approach.)

Avoid mixing numeric 1/0, Perl's native conditions, undef, and other schemes in the same function or library.

Finally, if you have ever done

$>  perl -wle 'print false && true'

you may have received

Unquoted string "false" may clash with future reserved word at -e line 1.
Unquoted string "true" may clash with future reserved word at -e line 1.
Bareword found in conditional at -e line 1.
true

so it appears likely that some day Perl may have an "official" scheme for Booleans, with values true and false.

I wonder how those will behave?

这篇关于Perl中的布尔函数应该返回什么值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆