在Perl中,return;和return undef;有什么区别?

29

有没有办法区分一个函数执行时传入参数的方式?例如,当函数被调用时,参数是按值传递还是按引用传递?

return;

还有一个能做到吗?

return undef;
4个回答

50

return;在列表上下文中返回一个空列表,但在标量上下文中返回一个undefreturn undef;始终会在任何上下文中返回单个值undef

通常情况下,从通常用于列表上下文的子程序中return undef;不是一个好主意:

sub foo { return undef }
if ( my @x = foo() ) {
    print "oops, we think we got a result";
}

通常情况下,从标量上下文中的子例程中return;并不是一个好主意,因为它在列表上下文中不会像用户期望的那样工作:

sub foo { return }
%x = ( 'foo' => foo(), 'bar' => 'baz' );
if ( ! exists $x{'bar'} ) {
    print "oops, bar became a value, not a key";
}

这两个错误在实践中经常发生,后者更常见,可能是因为预期返回标量的子程序更常见。如果预期返回标量,则最好返回标量。


1
这一切都源于胖逗号在右侧不会强制标量上下文的事实。在我看来,第二个例子中foo子程序的定义是完美的。失败是由于有一个子程序调用在胖逗号的右侧这种可疑的编码风格引起的。在右侧进行子程序调用(如您所示)可能会导致键/值对被破坏,因此这是一种脆弱的编码风格。 - null
@friedo的答案提供了简单而健壮的代码示例,展示了sub foo在标量上下文和列表上下文下返回预期值。但是sub bar在标量上下文下未能返回空列表。 - null
如果你允许子程序返回意外的结果,我想我会同意你的观点;但我的论点是,在第一次就允许这种情况是荒谬的。绝大多数子程序都会执行某些操作,其本质上要么返回一个标量,要么返回一个列表,而它们应该始终如一地这样做。 - ysth
@geira:永远不要使用隐式返回。 - ysth
正如我的示例所示,“die”是一个隐式返回。说任何函数都不应该死亡(包括segfault C库)并不是非常现实的。 - geira
显示剩余5条评论

23

给定

sub foo { return; }
sub bar { return undef; }
在标量上下文中,它们的行为相同。
my $foo = foo();   # $foo is undef
my $bar = bar();   # $bar is undef

在列表环境中,它们的行为不同

my @foo = foo();   # @foo is ()  (an empty list)
my @bar = bar();   # @bar is ( undef )  (a one-element list)

需要注意的是,在布尔上下文中,只有一个元素的列表是真值,即使唯一的元素是 undef

通常情况下,从子程序中返回 undef; 并不是一个好主意,因为它在上下文中的行为。


4
这就是为什么《Perl最佳实践》(以及 Perl::Critic)建议不要使用return undef的原因。 - hillu
6
有时候使用它是个好主意,原因与@hillu所说的相同。比如,一个函数应该返回逻辑(布尔)值,而你希望在另一个函数调用foo(1, bar(), 2)中使用它。如果bar写成了return;,那么你会感到惊讶;-) - Hynek -Pichi- Vychodil
1
PBP做得正确的事情之一是超过了几件。 - null

2

我认为我仍然同意PBP。

1)应避免嵌套函数调用:

my $result = bar();
foo(1, $result, 2);

2) 当进行“复杂”的操作时,您应始终明确说明:

foo(1, scalar bar(), 2);

  1. 这不仅仅是嵌套函数调用。
  2. 是的,从调用者的角度来看,这是一件好事。但从被调用者的角度来看,不能指望这种情况总是发生。
- ysth
更多的例子呢?无论如何,我相信你会更频繁地得到不稳定的结果: $ perl -wle 'sub get_values { return undef; }; my @values = get_values; print scalar @values;' 1如果你想更明确和规范一些,总有 Contextual::Return http://p3rl.org/Contextual::Return - nicomen
3
你的 get_values 函数旨在在列表上下文中使用,因此不应该 return undef。但是如果你有一个返回标量的 get_value 函数,它应该始终返回标量,而不是空列表,因此应使用 return undef。请参见 ysth 的答案。 - cjm

2
《Perl最佳实践》一书推荐使用return;而不是return undef;,因为后者在列表上下文中返回一个单元素列表(其他答案已经提到了这一点)。根据该书的说法,这将是一个“恶心的错误”。
然而,我认为返回空值实际上可能会导致严重的错误(可能很难找到),而在列表上下文中调用布尔函数似乎相当容易调试。
因此,我总是在我的布尔函数中返回something(通常为0表示false,1表示true)。
(是的,在列表上下文中,这也将返回一个单元素列表-因此书中的注释在技术上是正确的-但实际错误是在列表上下文中调用布尔函数。)
需要明确的是,我仍然推荐这本书。它提供了许多好的建议。
我参考的是《Perl最佳实践》一书第9章(子例程),第199/200页(“使用裸return返回失败。”),作者是Damian Conway(O'Reilly Media,Inc.),ISBN 978-0-596-00173-5。不确定是否有另一个/更新的版本。
附注:
当否定某些内容时,Perl似乎会返回一个空字符串。my $foo = 5; return !$foo;返回q()

1
!$foo 是空字符串,因为Perl使用1表示真,空字符串表示假。由于有!,所以您处于布尔上下文中。 - Andy Lester
不,´!5´是规范的假值,在字符串上下文中为"",在数值上下文中为0。 - ysth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接