我正在学习perl并且理解使用shift解包子例程参数是一种常见且公认的做法 . 我也明白,省略函数参数以使用默认的 @_
数组是常见且可接受的做法 .
考虑到这两件事,如果你调用一个没有参数的子程序, @_
可以(并且如果使用shift)可以改变 . 这是否意味着使用默认参数调用另一个子例程,或者事实上,在此之后使用 @_
数组被认为是不好的做法?考虑这个例子:
sub total { # calculate sum of all arguments
my $running_sum;
# take arguments one by one and sum them together
while (@_) {
$running_sum += shift;
}
$running_sum;
}
sub avg { calculate the mean of given arguments
if (@_ == 0) { return }
my $sum = &total; # gets the correct answer, but changes @_
$sum / @_ # causes division by zero, since @_ is now empty
}
我的直觉告诉我,使用shift来解包参数实际上是不好的做法,除非你的子程序实际上应该改变传递的参数,但我已经在多个地方读过,包括Stack Overflow,这不是一个坏习惯 .
所以问题是:如果使用shift是常见的做法,我应该总是假设传递的参数列表可以更改,作为子例程的副作用(如引用示例中的 &total
子例程)?有没有办法按值传递参数,所以我可以确定参数列表不会被更改,所以我可以再次使用它(比如引用文本中的 &avg
子例程)?
7 回答
一般来说,参数中的
shift
是正确的 - 使用&
sigil来调用函数isn 't. (Except in some very specific situations you' ll可能永远不会遇到 . )您的代码可以重写,因此
total
不会@_
来自@_
. 使用for循环甚至可能更有效 .或者您可以使用
List::Util
中的sum
函数:除了在面向对象的Perl中提取
$self
之外,使用shift
并不常见 . 但是因为你总是把你的函数称为foo( ... )
,所以无论是foo
shift
还是shift
都没有问题 .(关于函数唯一值得注意的是它是否分配给
@_
中的元素,因为这些是作为参数给出的变量的别名 . 分配给@_
中的元素通常是坏的 . )即使您无法更改
total
的实现,使用显式参数列表调用sub也是安全的,因为参数列表是数组的副本:(a)
&total
- 使用相同的@_
调用total
,并覆盖原型 .(b)
total(@_)
- 使用@_
的副本调用total
.(c)
&total(@_)
- 使用@_
的副本调用total
,并覆盖原型 .表格(b)是标准的 . 表格(c)不应该使用原型),并且由于某些不明原因必须覆盖它们 . 证明设计不佳 . 形式(a)仅适用于尾调用(
@_ = (...); goto &foo
)或其他形式的优化(过早优化是所有邪恶的根源) .你应该避免使用
&func;
的呼叫方式,除非你有充分的理由,并相信其他人也这样做 .要保护你的
@_
不被被调用者修改,只需做&func()
或func
.有时候Perl有点过于宽松,并且有多种方式访问输入参数可能会使代码变得异常和不一致 . 如果想要更好的答案,请尝试强加自己的标准 .
这是我用过和看过的几种方式
腥
扩大
@_
显式索引
使用函数原型强制执行参数(但这是not popular)
遗憾的是,您仍然必须执行自己的复杂输入验证/污点检查,即:
或者更好的是,提供更多信息
Perldoc perlsub应该是你的第一个停靠港 .
希望这可以帮助!
以下是一些谨慎使用
@_
问题的例子 .1. Hash-y Arguments
有时您想要编写一个可以获取键值对列表的函数,但其中一个是最常用的,您希望它在不需要键的情况下可用 . 例如
所以现在如果你用奇数个参数调用函数,第一个是位置 . 这允许
get_temp('Chicago')
或get_temp('New York', unit => 'C')
甚至get_temp( unit => 'K', location => 'Nome, Ak')
. 这可能是您的用户更方便的API . 通过移动奇数参数,现在@_
是偶数列表并且可以被分配给散列 .2. Dispatching
假设我们有一个类,我们希望能够按名称调度方法(可能AUTOLOAD可能很有用,我们将手动滚动) . 也许这是一个命令行脚本其中参数是方法 . 在这种情况下,我们定义两个调度方法一个"clean"和一个"dirty" . 如果我们用
-c
标志调用,我们就会得到干净的标志 . 这些方法按名称查找方法并调用它 . 不同的是如何 . 脏的一个离开自己在堆栈跟踪中,干净的一个必须是更多的切割器,但调度没有在堆栈跟踪中 . 我们制作了一个death
方法,为我们提供了跟踪 .所以现在如果我们调用
./test.pl
来调用死亡方法但是我们在踪迹中看到
dispatch_dirty
. 如果我们调用./test.pl -c
,我们现在使用干净的调度程序并获取这里的关键是goto(不是邪恶的goto),它使用子程序引用并立即使用当前
@_
将执行切换到该引用 . 这就是为什么我必须unshift @_, $self
,以便调用者为新方法做好准备 .参考文献:
HashWay:
我尝试了一个简单的例子:
这两个印刷语句的答案都是60 .
但我个人觉得,这些论点应该以这种方式接受:
为了避免后者出现任何问题 .
我在http://perldoc.perl.org/perlsub.html找到了以下宝石:
你可能遇到过这些问题之一:-(
OTOH amon可能是对的 - > 1