让我们暂时忽略Damian Conway对任何给定子程序不超过三个位置参数的最佳实践。
以下两个示例在性能或功能方面是否有任何区别?
使用shift
:
sub do_something_fantastical {
my $foo = shift;
my $bar = shift;
my $baz = shift;
my $qux = shift;
my $quux = shift;
my $corge = shift;
}
使用@_
:
sub do_something_fantastical {
my ($foo, $bar, $baz, $qux, $quux, $corge) = @_;
}
如果两个示例在性能和功能方面相同,那么人们对一种格式的看法是什么?显然,使用@_
的示例代码行较少,但使用shift
并不是更易读,如另一个示例所示?欢迎有充分理由的意见。
答案 0 :(得分:28)
有功能差异。 shift会修改@_
,@_
的分配则不会。如果您之后不需要使用@_
,那么这种差异可能对您无关紧要。我尝试始终使用列表分配,但有时我会使用shift
。
但是,如果我从shift
开始,就像这样:
my( $param ) = shift;
我经常创建这个错误:
my( $param, $other_param ) = shift;
那是因为我经常不使用shift
,所以我忘了转到作业的右侧,将其更改为@_
。这是不使用shift
的最佳做法。我可以像你在你的例子中那样为每个shift
制作单独的行,但这只是单调乏味。
答案 1 :(得分:16)
至少在我的系统上,它似乎取决于Perl和架构的版本:
#!/usr/bin/perl -w
use strict;
use warnings;
use autodie;
use Benchmark qw( cmpthese );
print "Using Perl $] under $^O\n\n";
cmpthese(
-1,
{
shifted => 'call( \&shifted )',
list_copy => 'call( \&list_copy )',
}
);
sub call {
$_[0]->(1..6); # Call our sub with six dummy args.
}
sub shifted {
my $foo = shift;
my $bar = shift;
my $baz = shift;
my $qux = shift;
my $quux = shift;
my $corge = shift;
return;
}
sub list_copy {
my ($foo, $bar, $baz, $qux, $quux, $corge) = @_;
return;
}
结果:
Using Perl 5.008008 under cygwin
Rate shifted list_copy
shifted 492062/s -- -10%
list_copy 547589/s 11% --
Using Perl 5.010000 under MSWin32
Rate list_copy shifted
list_copy 416767/s -- -5%
shifted 436906/s 5% --
Using Perl 5.008008 under MSWin32
Rate shifted list_copy
shifted 456435/s -- -19%
list_copy 563106/s 23% --
Using Perl 5.008008 under linux
Rate shifted list_copy
shifted 330830/s -- -17%
list_copy 398222/s 20% --
所以看起来list_copy通常比移位快20%,除了Perl 5.10,其中移位实际上稍快一点!
请注意,这些是快速得出的结果。实际速度差异大于此处列出的,因为Benchmark还会计算调用和返回子程序所需的时间,这将对结果产生调节作用。我没有做过任何调查,看看Perl是否正在进行任何特殊的优化。您的里程可能会有所不同。
保
答案 2 :(得分:8)
我认为移位示例比使用@_慢,因为它是6个函数调用而不是1.它是否显着或甚至可测量是一个不同的问题。将每个循环放入10k迭代循环中并计时。
至于美学,我更喜欢@_方法。使用带有意外剪切和粘贴的移位方法似乎太容易弄乱变量的顺序。另外,我见过很多人做过这样的事情:
sub do_something {
my $foo = shift;
$foo .= ".1";
my $baz = shift;
$baz .= ".bak";
my $bar = shift;
$bar .= ".a";
}
这个,恕我直言,非常讨厌,很容易导致错误,例如如果你切割baz块并将其粘贴在条块下面。我只是在变量附近定义变量,但我认为在函数顶部定义传入的变量优先。
答案 3 :(得分:4)
通常我会使用第一个版本。这是因为我通常需要与移位一起进行错误检查,这更容易编写。说,
sub do_something_fantastical {
my $foo = shift || die("must have foo");
my $bar = shift || 0; # $bar is optional
# ...
}
答案 4 :(得分:3)
我更喜欢将@_解压缩为列表(您的第二个示例)。虽然像Perl中的所有内容一样,但有些情况下使用shift会很有用。例如,用于在基类中重写的passthru方法,但是如果不覆盖它们,则要确保事物仍然有效。
package My::Base;
use Moose;
sub override_me { shift; return @_; }
答案 5 :(得分:2)
我更喜欢使用
sub do_something_fantastical {
my ( $foo, $bar, $baz, $qux, $quux, $corge ) = @_;
}
因为它更具可读性。如果不经常调用此代码,则值得。在极少数情况下,您需要经常调用make函数,而不是直接使用@_。它仅对非常短的函数有效,并且您必须确保此函数将来不会发展(一次写入函数)。在这种情况下,我在5.8.8中进行基准测试,单个参数的移位速度比$ _ [0]快,但使用$ _ [0]和$ _ [1]的两个参数比移位,移位更快。
sub fast1 { shift->call(@_) }
sub fast2 { $_[0]->call("a", $_[1]) }
但回到你的问题。我也更喜欢以这种方式对许多参数的移位进行一行@_赋值
sub do_something_fantastical2 {
my ( $foo, $bar, $baz, @rest ) = @_;
...
}
当Suspect @rest不会那么大。在其他情况下
sub raise {
my $inc = shift;
map {$_ + $inc} @_;
}
sub moreSpecial {
my ($inc, $power) = (shift(), shift());
map {($_ + $inc) ** $power} @_;
}
sub quadratic {
my ($a, $b, $c) = splice @_, 0, 3;
map {$a*$_*$_ + $b*$_ + $c} @_;
}
在极少数情况下我需要尾部调用优化(当然是手工)然后我必须直接使用@_,而不是短函数是值得的。
sub _switch #(type, treeNode, transform[, params, ...])
{
my $type = shift;
my ( $treeNode, $transform ) = @_;
unless ( defined $type ) {
require Data::Dumper;
die "Broken node: " . Data::Dumper->Dump( $treeNode, ['treeNode'] );
}
goto &{ $transform->{$type} } if exists $transform->{$type};
goto &{ $transform->{unknown} } if exists $transform->{unknown};
die "Unknown type $type";
}
sub switchExTree #(treeNode, transform[, params, ...])
{
my $treeNode = $_[0];
unshift @_, $treeNode->{type}; # set type
goto &_switch; # tail call
}
sub switchCompact #(treeNode, transform[, params, ...])
{
my $treeNode = $_[0];
unshift @_, (%$treeNode)[0]; # set type given as first key
goto &_switch; # tail call
}
sub ExTreeToCompact {
my $tree = shift;
return switchExTree( $tree, \%transformExTree2Compact );
}
sub CompactToExTree {
my $tree = shift;
return switchCompact( $tree, \%transformCompact2ExTree );
}
其中%transformExTree2Compact和%transformCompact2ExTree是key中的类型的哈希值和代码ref的值,可以尾调用switchExTree或switchCompact它自己。但这种方法很少真正需要,而且必须保持较低的价值。。
总之,可读性和可维护性必须特别在perl中,并且在一行中分配@_更好。如果你想设置默认值,你可以在它之后做。
答案 6 :(得分:1)
最好的方式,恕我直言,是两者的轻微混合,如模块中的新功能:
our $Class;
sub new {
my $Class = shift;
my %opts = @_;
my $self = \%opts;
# validate %opts for correctness
...
bless $self, $Class;
}
然后,构造函数的所有调用参数都作为哈希传递,这使得代码比仅仅参数列表更具可读性。
另外,与brian said一样,@_
变量未经修改,这在不寻常的情况下非常有用。
答案 7 :(得分:0)
我怀疑你是否正在做(粗略)相当于:
push @bar, shift @_ for (1 :: $big_number);
然后你做错了什么。我总是使用my ($foo, $bar) = @_;
形式,因为我用足够的次数拍摄了自己的脚... ...
答案 8 :(得分:0)
我更喜欢
sub factorial{
my($n) = @_;
...
}
由于简单的原因,科莫多将能够告诉我论证的内容,当我去使用它时。