sub foo {
$arg1 = shift @_;
$arg2 = shift @_;
# ...
}
这个成语有什么好处?与明确使用$_[0]
,$_[1]
相比,我认为只有缺点...数组必须移位,这非常耗时。它被破坏了,所以在稍后的时间点,参数已经消失了(如果你再次需要它们并且用不同的值覆盖你的$ arg1,那就很难过。)
答案 0 :(得分:9)
在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)
答案 1 :(得分:5)
直接访问$_[0]
等比使用命名参数 [1] 更快,但是有很多理由更喜欢命名参数。
到目前为止,最重要的原因是使用命名参数作为一种文档形式。
直接使用@_
很难阅读,因为很难跟踪哪个参数具有什么价值。
例如,“$_[4]
包含哪些内容?是行吗?还是$_[5]
?”
偶然使用错误的索引很容易。
由于符号数量的原因,直接使用@_
很难阅读。
比较
grep { $_[0]{$_} =~ /.../ } keys %{$_[0]}
与
grep { $foos->{$_} =~ /.../ } keys %$foos
提供默认值非常有用。
sub f {
my $x = shift // "abc";
my $y = shift // "def";
...
}
复制到标量有效地引入了逐个拷贝的语义,这可能很有用。
$ 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
注意:
是否是我的首选
sub f {
my (...) = @_;
...
}
或
sub f {
my ... = shift;
my ... = shift;
...
}
答案 2 :(得分:4)
我同意所有观点。但基本问题仍然存在:我为什么要转移而不是进行列表赋值或一系列标量赋值?
由于我做了转移,我会解释为什么要这样做。
有三种方法可以处理子程序的参数:
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开发人员也是如此。它为您提供了一个参数列表,并为您提供了一种方式来说明这些是我的参数而没有其他。
但是,方法2 和方法3 为您提供了一个简单易读的参数列表,您可以在该行的末尾添加注释。但是,方法2 还具有保持@_
值的优势,那么为什么要使用shift
?
再次查看方法2 。看到问题?我从@_[1]
而不是@_[0]
开始 - 这是一个常见错误。另外,在开发子程序时,您可能决定重新排序参数。使用方法2 表示重新编号。 方法3 不需要重新编号,因此您最终不会这样做:
sub foo {
my $date = $_[1]; # Must be YYYY-MM-DD
my $user = $_[0];
my $system = $_[2]; # Optional
嗯,再次参数的顺序是什么?
那么,保留@_
的价值呢?如果您编写一个更改参数值的程序,则可能不会从@_
恢复它。而且,如果你这样做,你可能会产生一些容易出错的代码。如果你需要munge一个参数,把它放在另一个变量中并进行操作。这不是1975年计算机只有4千字节的内存。
而且,谈到20世纪70年代,计算机足够快,以至于少数(十二,一百,几千)个班次操作的时间量不会在总运行时间上产生那么大的差异。如果您的程序效率低下,请在效率低下的地方进行优化,而不是在几毫秒内消除轮班。维护程序所花费的时间比实际运行时间要长。
Damian Conway(Perl Best Practices的作者)建议方法#1 和方法#3 。他声明方法#1 是“...更简洁,它将参数保存在一个水平列表中,这增强了可读性,只要参数的数量是小。“(重点是我的)。
Damian说明了方法#3 :“无论何时,只要一个或多个参数必须经过健全检查或需要记录尾随,基于班次的版本更可取评论“。
我一直只使用方法#3 。这样,如果我的参数列表增长,我不必担心重新格式化,我只是认为它看起来更好。
答案 3 :(得分:0)
在对象类中编写方法时,我总是首先shift
调用@_
调用,然后将剩余的参数解包,从而保留所有其他非调用参数。这允许呼叫站点看起来更像是方法定义本身,就其中的内容而言。
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( @_ );
}