Переменная «arrv» не может иметь индекс, поскольку она не является массивом.
Существует функциональное различие. Сдвиг изменяет @_
, и присвоение от @_
не делает. Если Вы не должны использовать @_
позже, то различие, вероятно, не имеет значения для Вас. Я пытаюсь всегда использовать присвоение списка, но я иногда использую shift
.
Однако, если я начинаюсь с shift
, как так:
my( $param ) = shift;
Я часто создаю эту ошибку:
my( $param, $other_param ) = shift;
Поэтому я не использую shift
это часто, таким образом, я забываю преобладать к правой стороне присвоения для изменения этого на @_
. В этом суть лучшей практики в не использовании shift
. Я мог сделать отдельные строки для каждого shift
когда Вы сделали в своем примере, но это просто утомительно.
По крайней мере, в моих системах, это, кажется, зависит от версии 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, где смещение на самом деле немного быстрее!
Обратите внимание, что они были быстро получены результаты. Различия в действительной скорости будут больше, чем, что перечислено здесь, так как Сравнительный тест также считает время потраченным, чтобы назвать и возвратить подпрограммы, которые будут иметь эффект модерирования на результаты. Я не сделал никакого расследования, чтобы видеть, делает ли Perl какой-либо специальный вид оптимизации. Ваш пробег может варьироваться.
Paul
Я предположил бы, что пример сдвига медленнее, чем использование _, потому что это - 6 вызовов функции вместо 1. Примечательно ли это, или даже измеримый другой вопрос. Бросьте каждого в цикл 10k повторений и время их.
Что касается эстетики, я предпочитаю _ метод. Кажется, что было бы слишком легко испортить порядок переменных с помощью метода сдвига с вырезанным и вставленным случайным элементом. Кроме того, я видел, что многие люди делают что-то вроде этого:
sub do_something {
my $foo = shift;
$foo .= ".1";
my $baz = shift;
$baz .= ".bak";
my $bar = shift;
$bar .= ".a";
}
Это, по моему скромному мнению, очень противно и могло легко привести к ошибкам, например, если Вы сокращаете baz блок и вставляете его под блоком панели. Я - все для определения переменных рядом, где они используются, но я думаю, определяя переданный в переменных во главе функции, имеет приоритет.
Обычно я использую первую версию. Это вызвано тем, что у меня обычно должна быть проверка ошибок вместе со сдвигами, которую легче записать. Скажите,
sub do_something_fantastical {
my $foo = shift || die("must have foo");
my $bar = shift || 0; # $bar is optional
# ...
}
Я предпочитаю распаковывать _ как список (Ваш второй пример). Хотя, как все в Perl, существуют экземпляры, где использование сдвига может быть полезным. Например, методы passthru, которые предназначаются, чтобы быть переопределенными в базовом классе, но Вы хотите удостовериться, что вещи все еще работают, если они не переопределяются.
package My::Base;
use Moose;
sub override_me { shift; return @_; }
Я предпочитаю использовать
sub do_something_fantastical {
my ( $foo, $bar, $baz, $qux, $quux, $corge ) = @_;
}
Поскольку это более читаемо. Когда к этому коду не называют очень часто, это стоит пути. В очень редких случаях Вы хотите, делают функцию называемой часто и, чем использование _ непосредственно. Это эффективно только для очень коротких функций, и необходимо быть уверены, что эта функция не разовьется в будущем (Запись однажды функция). Я этот случай, которого я сравнил в 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 ) = @_;
...
}
Когда Подозреваемый @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 являются хешами с типом в ключе и коде касательно в значении, которое может выследить вызов switchExTree или switchCompact он selfs. Но этот подход является редко действительно потребностью и должен сохранить меньше стоящим пальцев колледжа прочь.
В заключение удобочитаемость и пригодность для обслуживания, должен особенно в жемчуге, и присвоение _ в одной строке лучше. Если Вы хотите значения по умолчанию набора, можно сделать это сразу после него.
Я подозреваю, делаете ли Вы (грубый) эквивалент:
push @bar, shift @_ for (1 :: $big_number);
Затем Вы делаете что-то не так. Я amost всегда использую my ($foo, $bar) = @_;
сформируйтесь, потому что я выстрелил себе в ногу с помощью последнего несколько слишком много раз...
Наилучший способ, IMHO, представляет собой небольшое смешение этих двух, как в новой функции в модуле:
our $Class;
sub new {
my $Class = shift;
my %opts = @_;
my $self = \%opts;
# validate %opts for correctness
...
bless $self, $Class;
}
Затем все вызывающие аргументы в конструктор передаются в виде хэша, что делает код более читаемым чем просто список параметров.
Кроме того, как сказал Брайан , переменная @_
не изменена, что может быть полезно в необычных случаях.
Я предпочитаю
sub factorial{
my($n) = @_;
...
}
По той простой причине, что Komodo сможет рассказать мне, каковы аргументы, когда я буду использовать его.