在perl中,使用默认参数调用多个子例程是一种不好的做法吗?
我正在学习perl,并了解使用shift解包子程序参数是一种常见且公认的做法。我还理解,省略函数参数以使用默认的在perl中,使用默认参数调用多个子例程是一种不好的做法吗?,perl,subroutine,shift,Perl,Subroutine,Shift,我正在学习perl,并了解使用shift解包子程序参数是一种常见且公认的做法。我还理解,省略函数参数以使用默认的@数组是一种常见且可接受的做法 考虑到这两件事,如果您在没有参数的情况下调用一个子例程,@可以(并且如果使用shift)更改。这是否意味着使用默认参数调用另一个子例程,或者实际上在此之后使用@数组,被认为是不好的做法?考虑这个例子: sub total { # calculate sum of all arguments my $running_sum; # take
@
数组是一种常见且可接受的做法
考虑到这两件事,如果您在没有参数的情况下调用一个子例程,@
可以(并且如果使用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解压参数实际上是一种不好的做法,除非您的子例程实际上应该更改传递的参数,但我在多个地方读到过,包括堆栈溢出,这不是一种不好的做法
所以问题是:如果使用shift是常见的做法,我是否应该始终假定传递的参数列表可能会发生更改,这是子例程的副作用(如引用的示例中的
&total
子例程)?是否有一种方法可以按值传递参数,这样我就可以确保参数列表不会被更改,这样我就可以再次使用它(如引用文本中的&avg
子例程)?Perl有时有点太松散,使用多种访问输入参数的方法可能会使代码变得难闻和不一致。要想得到更好的答案,试着强加你自己的标准
以下是我使用和看到的一些方法
狡猾的
sub login
{
my $user = shift;
my $passphrase = shift;
# Validate authentication
return 0;
}
展开@
sub login
{
my ($user, $passphrase) = @_;
# Validate authentication
return 0;
}
显式索引
sub login
{
my user = $_[0];
my user = $_[1];
# Validate authentication
return 0;
}
使用函数原型强制执行参数(但这是一个错误)
遗憾的是,您仍然必须执行自己复杂的输入验证/污染检查,即:
return unless defined $user;
return unless defined $passphrase;
或者更好的是,提供更多的信息
unless (defined($user) && defined($passphrase)) {
carp "Input error: user or passphrase not defined";
return -1;
}
应该是你的第一个停靠港
希望这有帮助 参考文献:
sub refWay{
my ($refToArray,$secondParam,$thirdParam) = @_;
#work here
}
refWay(\@array, 'a','b');
哈什韦:
sub hashWay{
my $refToHash = shift; #(if pass ref to hash)
#and i know, that:
return undef unless exists $refToHash->{'user'};
return undef unless exists $refToHash->{'password'};
#or the same in loop:
for (qw(user password etc)){
return undef unless exists $refToHash->{$_};
}
}
hashWay({'user'=>YourName, 'password'=>YourPassword});
一般来说,
shift
使用&
符号调用函数是可以的,而不是。(除非在某些非常特殊的情况下,您可能永远不会遇到。)
您的代码可以被重新编写,这样总计
就不会从@
移动
。使用for循环可能更有效
sub total {
my $total = 0;
$total += $_ for @_;
$total;
}
或者您可以使用List::Util
中的sum
函数:
use List::Util qw(sum);
sub avg { @_ ? sum(@_) / @_ : 0 }
除了在面向对象的Perl中提取$self
之外,使用shift
并不常见。但是,由于您总是调用函数,如foo(…)
,因此foo
shift
s或不shift
参数数组并不重要。(关于函数,唯一值得注意的是它是否分配给
@
中的元素,因为这些是作为参数提供的变量的别名。分配给@
中的元素通常是不好的。)
即使无法更改total
的实现,使用显式参数列表调用sub也是安全的,因为参数列表是数组的副本:
(a) &total
-使用相同的@
调用total
,并覆盖原型。(b)
total(@)
-调用total
,并附上@
(c)
&total(@)
-使用@
的副本调用total
,并覆盖原型
表格(b)为标准表格。表格(c)不应该被看到,除非在同一个包中的sub有一个原型(并且不使用原型),并且由于一些模糊的原因,它们必须被覆盖。拙劣设计的证明。
形式(a)只适用于尾部调用(
@=(…);goto&foo
)或其他形式的优化(过早优化是万恶之源)。我尝试了一个简单的例子:
#!/usr/bin/perl
use strict;
sub total {
my $sum = 0;
while(@_) {
$sum = $sum + shift;
}
return $sum;
}
sub total1 {
my ($a, $aa, $aaa) = @_;
return ($a + $aa + $aaa);
}
my $s;
$s = total(10, 20, 30);
print $s;
$s = total1(10, 20, 30);
print "\n$s";
两份书面声明的答案都是60
但我个人认为,应该以这种方式接受这些论点:
my (arguments, @garb) = @_;
为了避免以后出现任何形式的问题。我在中发现了以下宝石: “是的,还有一些与@的可见性有关的未解决问题。我暂时忽略了这个问题。(但请注意,如果我们将@的范围设为词汇范围,那么这些匿名子例程可以像闭包一样工作…(哎呀,这听起来有点含糊不清吗?(没关系。))” 您可能遇到以下问题之一:-(
奥托·阿蒙可能是对的->+1这里有一些例子说明谨慎使用
@
很重要
1.散列y参数
有时,您希望编写一个函数,该函数可以获取键值对列表,但其中一个是最常用的,您希望该函数在不需要键的情况下可用
sub get_temp {
my $location = @_ % 2 ? shift : undef;
my %options = @_;
$location ||= $options{location};
...
}
因此,如果您使用奇数个参数调用函数,第一个参数是location。这允许get_temp('Chicago')
或get_temp('New York',unit=>'C')
或偶数get_temp(unit=>'K',location=>'Nome,Ak'))
。对于用户来说,这可能是一个更方便的API。通过移动奇数参数,现在@
是一个偶数列表,可以分配给散列
2.调度
假设我们有一个类,我们希望能够按名称分派方法(可能自动加载可能很有用,我们将手动滚动)。也许这是一个命令行脚本,其中参数是方法。在本例中,我们定义了两个分派方法,一个是“clean”,另一个是“dirty”。如果我们使用-c
标记调用,我们将得到干净的方法。这些方法通过名称找到方法并调用它。区别在于如何。脏的方法将自身留在堆栈跟踪中,干净的方法必须更清晰,但不在堆栈跟踪中进行调度。我们创建了一个death
方法来提供该跟踪
#!/usr/bin/env perl
use strict;
use warnings;
package Unusual;
use Carp;
sub new {
my $class = shift;
return bless { @_ }, $class;
}
sub dispatch_dirty {
my $self = shift;
my $name = shift;
my $method = $self->can($name) or confess "No method named $name";
$self->$method(@_);
}
sub dispatch_clean {
my $self = shift;
my $name = shift;
my $method = $self->can($name) or confess "No method named $name";
unshift @_, $self;
goto $method;
}
sub death {
my ($self, $message) = @_;
$message ||= 'died';
confess "$self->{name}: $message";
}
package main;
use Getopt::Long;
GetOptions
'clean' => \my $clean,
'name=s' => \(my $name = 'Robot');
my $obj = Unusual->new(name => $name);
if ($clean) {
$obj->dispatch_clean(@ARGV);
} else {
$obj->dispatch_dirty(@ARGV);
}
现在如果我们调用/test.pl
来调用death方法
$ ./test.pl death Goodbye
Robot: Goodbye at ./test.pl line 32
Unusual::death('Unusual=HASH(0xa0f7188)', 'Goodbye') called at ./test.pl line 19
Unusual::dispatch_dirty('Unusual=HASH(0xa0f7188)', 'death', 'Goodbye') called at ./test.pl line 46
但是我们可以在中看到dispatch\u dirty
$ ./test.pl death Goodbye
Robot: Goodbye at ./test.pl line 32
Unusual::death('Unusual=HASH(0xa0f7188)', 'Goodbye') called at ./test.pl line 19
Unusual::dispatch_dirty('Unusual=HASH(0xa0f7188)', 'death', 'Goodbye') called at ./test.pl line 46
$ ./test.pl -c death Adios
Robot: Adios at ./test.pl line 33
Unusual::death('Unusual=HASH(0x9427188)', 'Adios') called at ./test.pl line 44