[英]What values should a boolean function in Perl return?
簡短的問題
在Perl代碼庫中一致表示真假的最佳方法是什么?
1
/ 0
?
1
/ Perl的本機布爾運算符的特殊空字符串
返回?
undef
?
()
(即空列表)?
問題背景
我們都知道Perl在布爾方面非常靈活,就像大多數事情一樣。
例如,Perl將以下內容視為false: undef()
,數字0
(即使寫為000或0.0),空字符串, '0'
(包含單個0位的字符串)。 Perl將以下內容視為true:任何其他標量值, 1
, -1
,帶有空格的字符串( ' '
), '00'
字符串中'00'
多個0, "0\\n"
(a'0'后跟換行符, 'true'
, 'false'
, 'undef'
等。另外,數組到標量和列表到標量的轉換意味着你可以經常使用空數組作為假值。 (感謝http://perlmaven.com/boolean-values-in-perl獲取Perl接受為真或假的標量值的部分列表。)
但是考慮到所有這些被視為true或false的值,Perl布爾函數應該返回什么?
1/0
-1/0
( -1/0
) 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時)。
我返回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
}
}
如果在標量上下文中調用它,這可以正常工作。
if (some_boolean()) {
...
} else {
...
}
一切都按預期工作。 但如果你在列表上下文中調用它,事情就會變得有些奇怪。
my @array = some_boolean();
if (@array) {
...
} else {
...
}
在這種情況下,永遠不會調用else
塊。 在列表上下文中,子例程返回包含單個標量(值undef
)的列表,因此@array
具有單個元素, if (@array)
測試始終為true。
當然,您的子例程並不打算在列表上下文中調用。 但你無法控制其他程序員如何使用你的代碼。
但如果子程序是這樣寫的:
sub some_boolean {
if ($some_condition) {
return 1;
} else {
return;
}
}
一切都會按預期工作。 如果在標量上下文中調用子例程,它將返回標量false值,如果在列表上下文中調用它,它將返回一個空列表。
在我看來,從子例程返回顯式的偽標量值始終是可疑的。
如果你想返回一個顯式的標量假值,那么檢查調用上下文並采取適當的操作是非常值得的。
croak 'Subroutine called in list context.' if wantarray;
更新:回答此評論。
IIRC我嘗試使用這種方法,但放棄了它,因為它產生了更多關於未定義值的警告 - 即它不是現有Perl 1 / !! 0比較的替代品,也不是1/0。 考慮
perl -wle 'print "a()=<<".a().">>\\n"; sub a {if(@_) {return 1} else {return}} '
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}}'
我只想重申我原來的觀點。 如果您返回false的任何標量值,並且有任何方法可以在列表上下文中調用子例程(甚至意外),那么您已經在代碼中引入了潛在的錯誤。
僅此一點,在打印它們之前必須解碼布爾值是非常值得的。
--- +簡短的回答
以下是Perl中布爾人的兩個自洽方案:
1/0
- 可打印和便攜 1/!!0
- 最像Perl的本機布爾函數 1/0
可能是其他語言的程序員最熟悉的,比如C或Python。 您可以打印1/0
布爾值,添加它們,依此類推。 但是... Perl的原生布爾運算符不是1/0
值, return 1 if $a<0; return 0
,則return $a<0
與return 1 if $a<0; return 0
return 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/()
從其他布爾方案轉換為1/()
($other_bool?1:undef)
從其他布爾方案轉換為1/undef
問:除了?:
之外還有更短或者前綴的符號?:
?
還有一些可能的方案
1/()
- 更准確地說, 返回1或沒有 - 可以捕獲 (1)/(0)
- 返回長度為1的列表,其中包含1或空列表。 類似於1/()
,在true和false都是數組的意義上是一致的。 1/undef
- 另一種可能性,捕獲1/()
返回1或沒有任何可能導致的錯誤 我毫不猶豫地推薦這些。 至少, 1/()
是不一致的但是......它們當然已經被Perl程序員使用了,所以你應該准備好處理使用這些方案的代碼。 即准備調試由這些方案引起的錯誤。
1/()
是我嘗試為一個方案創建一個縮寫名稱,其中返回true的函數return 1;
,返回false的函數return;
,即沒有操作數值的返回。 即返回1或沒有 。 我認為返回什么都不等於return ();
。 此方案可以保護您免受程序員在列表上下文而不是標量上下文中評估函數所導致的錯誤。 但是它暴露給你的錯誤,例如{1=>return_nothing_if_false(),2=>return_nothing_if_false()}
(你可能不希望{1=>2}
。
順便說一下 ,我認為這可能更符合方案(1)/()
。 這將允許您一致; y具有此布爾類型的變量,即使是@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"}
如果兩者都為假,則{a=>1,b=>1}
如果兩者都為真。 至少如果f1返回true並且f2返回false,反之亦然,則會收到關於奇數大小哈希的警告。
調用函數的程序員可以控制它是在列表還是標量上下文中進行計算,但是她可能無法控制它是返回true還是false。
恕我直言,如果你做1/()
, (1)/()
或1 / undef你不能安全地在數組上下文中調用這樣的函數,比如構建關鍵字參數foo(kw=>boolfunc(),...)
或foo({kw=>boolfunc(),kw2=>...},...)
。 不必分散! 或者全都是0+。
--- +中等長度答案
概括原答案:
Perl有許多表達真理的方法; 或者更確切地說,Perl將許多不同的值解釋為真或假。
如果您正在創建一系列相關功能,即。 在圖書館,建議您選擇以下眾所周知的方案之一,並在您的圖書館中始終如一地使用它:
真相1/0
- 數字 - 最容易進出其他語言,並且更易於打印
真相1/!!0
- 最像標准的Perl關系運算符,便攜性較差,可打印性較差(除非你想假看不可見)
這個答案強調布爾函數或方法,謂詞。 它不是試圖討論非布爾函數,它返回實際的東西,如數字或字符串或引用 - 除了下面簡要說明。
@DaveCross提出了另一個有趣的方案
return 1
/ return
nothing(差不多1/()
,空列表) 我記得從Perl的早期開始的一個方案 - 在refs之前,我想甚至在undef
之前是一個可以返回的值。 但是IIRC有這個方案的問題和你可能的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用於undefs和字符串的庫,可能會或可能不會被視為錯誤或失敗 如果查看返回ref或undef的函數的返回值
如果1/!!0
Perl-like Booleans,則返回!!ref_retval_func()
或defined ref_retval_func()
如果1/0
更多便攜式布爾值,則返回0+!!ref_retval_func()
或0+(defined ref_retval_func())
下面的方法太詳細了。
--- ++可能嗎?:返回1或不返回任何方案(可能是1 /()?)
@DaveCross提出了一個有趣的建議:
返回1表示布爾值。
不為布爾值false返回任何內容。 那是因為裸露
return將返回一個適當的值,具體取決於如何
子程序已被調用。 退貨文件說明了這一點:
如果沒有給出EXPR,則在列表上下文中返回空列表,在標量上下文中返回未定義的值,並且(當然)在void上下文中根本不返回任何內容。
--- ++反建議:不要混合布爾方案例如,在同一個函數或庫中,不要這樣做
return $arg1 < $arg2; # returning a standard Perl 1/!!0 Boolean
在一個地方,然后在其他地方,或在以后的相同代碼的演變,做
return 0;
return undef;
return '';
return ();
即選擇一個布爾方案,並保持一致。 主要是,這涉及到虛假價值的一致性; 在較小程度上的真實價值。
--- +過多的MESSY細節
--- ++在其他地方討論Perl的許多真理價值觀
諸如返回布爾值的Perl函數實際返回的帖子以及為什么Perl使用空字符串來表示布爾值false? 討論Perl布爾函數和運算符實際返回的內容。 基本上是特殊值,其行為由Perl手冊指定。
@cim鏈接到perl手冊: http ://perldoc.perl.org/perlsyn.html#Truth-and-Falsehood
真相與虛假
在布爾上下文中,數字0,字符串'0'和“”,空列表()和undef都是false。 所有其他值都是真的。 否定真正的價值! 或不返回特殊的假值。 當被評估為字符串時,它被視為“”,但作為數字,它被視為0.大多數返回true或false的Perl運算符都以這種方式運行。
同樣http://perldoc.perl.org/perlop.html#Relational-Operators
關系運算符
返回true或false的Perl運算符通常返回可安全用作數字的值。 例如,本節中的關系運算符和下一個中的相等運算符返回1表示true,定義空字符串的特殊版本“”,計為零,但不受有關不正確數字轉換的警告,因為“0但是真實”是。
不幸的是, 返回布爾實際返回的Perl函數的接受的答案討論了內部,但隨后建議
my $formatted = $result ? '1' : '0';
這是回到我們開始的地方。
@amon在關於問題的評論中向我們展示了光(!!)。 返回布爾實際返回的Perl函數是什么
旁注:您可以將任何值轉換為具有雙重否定的相應布爾值。 這導致!! 偽操作。 非常有用的返回通用truthy或falsey值而不是一些幻數。 - amon 11月22日在22:11
這些特殊的布爾人似乎沒有任何字面意思。 然而,有許多方法可以產生它們: (0<0)
, (0<1)
等等(!!1)
和(!!0)
可能是最好的 - 特別是因為在某些C / C ++編程圈子中它們用於類似的目的。 另外, !!
可以應用於傳入的真值,以將其“規范化”為此“Perl標准”布爾值。
--- ++反建議:不要混合布爾方案例如,在同一個函數或庫中,不要這樣做
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-ish布爾運算符中演化代碼
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
--- ++從value / 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,也不是一致的值/ undef。
(注意:我並不是說這種預檢是一種性能優化---但我說性能優化可能與上述類似。性能優化是我的專長之一,你希望這樣的優化能夠重構。它很糟糕當優化的代碼執行得更好時,但在使用它的某些地方會中斷。因此我對代碼的興趣與...完全一樣......無論它是什么樣的替換,就像本地的Perl關系運算符一樣。完全正確地意味着。)
如果您使用標准的Perl-ish布爾值,請執行以下操作。
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的'native'布爾值。
例如
sub my_boolean_function {
...
return !!1; # true
...
return !!0; # false
}
** --- + 0/1 - > 1 / !! 0轉換**
如果你考慮!! 作為從“meta-boolean”到“special boolean”的轉換,
將1 *或0+視為從特殊布爾值到普通0/1布爾值的轉換。
例如print "test".(1*($a eq $b))."\\n"
例如print "test".(0+($a eq $b))."\\n"
?:更通用,但更冗長。
--- ++非布爾錯誤返回
這個問題和答案強調布爾函數或方法,謂詞。 它不是試圖討論非布爾函數,它返回實際的東西,如數字或字符串或引用 - 除了下面簡要說明。
將返回值擴展為指示特殊條件(例如失敗,無效輸入等)並且可以在IF語句或其他控制流(例如和和或運算符)的上下文中進行評估,這通常是處理“很好”此類錯誤,例如提供默認值。
我們將對非布爾函數的討論限制在這個簡短的列表中:
ref
/ undef
:用於返回典型的http://perldoc.perl.org/perlobj.html對象的函數,對於有福的散列或其他類型的引用。 錯誤時返回undef
,未找到等等。
任何值/ undef
:用於返回任何類型的值,標量數字或字符串的函數,標量引用無論是有福的還是未經過的。
當undef
不是合法的返回值時,value / 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 /()?)
@DaveCross提出了一個有趣的建議:
返回1表示布爾值。
不為布爾值false返回任何內容。 那是因為裸露
return將返回一個適當的值,具體取決於如何
子程序已被調用。 退貨文件說明了這一點:
如果沒有給出EXPR,則在列表上下文中返回空列表,在標量上下文中返回未定義的值,並且(當然)在void上下文中根本不返回任何內容。
這很重要,因為在標量和列表上下文之間,true和false值可能會略有不同。 想象一下像這樣的子程序:......
@DaveCross繼續展示如果在數組上下文中評估布爾函數,返回除空列表之外的任何值都會導致失去錯誤。 甚至@array=(undef)
計算結果為true。
我希望這個計划能夠奏效。 我認為我幾年前在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()=<<>>
%
--- + BOTTOM LINE
使用1/0
(可打印和便攜式), 1/0!!
(最像Perl的原生布爾函數)。
可能return 1
或不return
任何內容,這幾乎與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可能會為布爾人制定一個“官方”計划,其價值是真的和假的。
我想知道那些會如何表現?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.