sub foo {
$arg1 = shift @_;
$arg2 = shift @_;
# ...
}
这个习语有什么好处吗?与明确使用
$_[0]
、$_[1]
等相比,我只看到了缺点。需要对数组进行移动,这是耗时的。数组还被销毁了,因此在以后的某个时间点,这些参数就消失了(如果您需要它们,并且已经用不同的值覆盖了$arg1,则很遗憾)。@_
是在 OO Perl
中常见的,这样可以将参数与类的实例分离开来,而该实例会自动添加为 @_
的第一个元素。sub foo {
my $arg1 = shift // 2;
my $arg2 = shift // 7;
# ...
}
我认为与显式地使用 $[0], $1 等相比,只有劣势。
不要使用 $_[0], $_[1]
进行操作,一次性把整个 @_
数组赋值是更好 / 更少出错 / 更易读的实践方法。
my ($arg1, $arg2) = @_;
请注意,@_
元素是传递变量的别名,因此可能会发生意外更改。
sub foo {
$_[0] = 33;
$_[1] = 44;
}
my ($x, $y) = (11,22);
print "($x, $y)\n";
foo($x, $y);
print "($x, $y)\n";
输出
(11, 22)
(33, 44)
直接访问$_[0]
等,比使用命名参数[1]更快,但是有很多理由倾向于使用命名参数。
By far the most important reason, using named parameters serves as a form of documentation.
Using @_
directly is hard to read because it's hard to keep track of which argument has what value.
For example, "What does $_[4]
contain? Is it the rows? Or is that $_[5]
?"
It's easy to use the wrong index by accident.
Using @_
directly is hard to read because of the amount of symbols.
Compare
grep { $_[0]{$_} =~ /.../ } keys %{$_[0]}
with
grep { $foos->{$_} =~ /.../ } keys %$foos
It's useful for providing defaults.
sub f {
my $x = shift // "abc";
my $y = shift // "def";
...
}
Copying into a scalar effectively introduces pass-by-copy semantics, which can be useful.
$ perl -le'my $x=123; sub f { $x=456; print $_[0]; } f($x);'
456
$ perl -le'my $x=123; sub f { my $arg = shift; $x=456; print $arg; } f($x);'
123
Whether it's my preferred
sub f {
my (...) = @_;
...
}
or
sub f {
my ... = shift;
my ... = shift;
...
}
$_[0]
、$_[1]
的好处。那是完全不同的问题,评论区不是提问的地方。 - ikegami@_
则不会?”(无论是直接使用它,通过列表赋值还是通过一系列赋值)。现在有这么多人回答了,我不想再问这个问题了。 - ubuplex sub foo {
my ( $user, $date, $system ) = @_; # No shifting, and @_ preserved
sub foo {
my $user = $_[1];
my $date = $_[2]; # Must be YYYY-MM-DD
my $system = $_[3]; # Optional
sub foo {
my $user = shift;
my $date = shift; # Must be YYYY-MM-DD
my $system = shift; # Optional
方法 1 很受欢迎。Ikegami 使用它,许多其他高级 Perl 开发人员也使用它。它为您提供参数列表,并提供了一种说“这些是我的参数,没有其他参数”的方式。@_
值的优点,那么为什么要使用 shift
?@_[1]
开始,而不是从 @_[0]
开始——这是一个常见的错误。此外,在开发子例程时,您可能会决定重新排序参数。使用 方法 2 意味着需要重新编号。而 方法 3 不需要重新编号,因此您不会遇到这种情况: sub foo {
my $date = $_[1]; # Must be YYYY-MM-DD
my $user = $_[0];
my $system = $_[2]; # Optional
嗯,参数的顺序是什么来着?
那么,保留@_
的值怎么办呢?如果你编写了一个更改参数值的程序,那么你可能不会从@_
中还原它。而且,如果你这样做,你可能会产生一些容易出错的代码。如果需要更改参数,请将其放入另一个变量中并进行更改。这不是1975年,计算机只有4千字节的内存。
说到1970年代的计算机,计算机的速度已经足够快了,即使进行几十、几百或者几千次移位操作,对于您的总运行时间也不会有太大影响。如果您的程序效率低下,请在效率低下的地方进行优化,而不是通过可能消除移位来缩短几毫秒的时间。与实际运行程序相比,更多的时间将花费在维护程序上。
Damian Conway(Perl Best Practices作者)推荐使用方法1和方法3。他表示方法1更为简洁,将参数保持在水平列表中有助于提高可读性,前提是参数数量较少。(强调为本人所加)。
Damian关于方法3表示:"当一个或多个参数需要进行合理性检查或需要在尾注释中记录时,使用基于shift的版本更好。"
我通常都使用方法3。这样,如果我的参数列表增长,我就不用担心重新格式化,而且我认为它看起来更好。
$_[0]/$n=$_[0]/($n)=@_/$n=shift
的情况下,运行时间为244/246/244/245秒。所以你是对的。我接受这个答案。(当然,迭代版本只需0.00秒。) - ubuplex@ _
中移除,然后仅展开其余参数,从而保留所有其他非调用者参数。 这使得调用站点在括号内看起来更像方法定义本身。sub method
{
my $self = shift;
my ( $x, $y, $z ) = @_;
}
$obj->method( "x", "y", "z" );
@_
本身即可:sub do_thing_with_whatsit
{
my $self = shift;
$self->{whatsit}->do_thing( @_ );
}
@_
中的更改会更改您的参数,这是您不想要的副作用。此外,移位文档记录了您期望的参数。您可以使用my ($param1, $param2) = @_;
,但我更喜欢使用shift
,通常我会使用my $var = shift;
并隐式地使用@_
而不是在my $var = shift @_;
中显式使用它。 - David W.@_
,这意味着如果我搞砸了,就无法恢复原始参数。我主要是出于 美观 的考虑这样做的。这使得很容易看到参数是什么,并且您可以在末尾添加注释以解释每个参数。 - David W.