Существует ли различие между сдвигом Perl по сравнению с присвоением от _ для параметров подпрограммы?

Переменная «arrv» не может иметь индекс, поскольку она не является массивом.

15
задан John Siracusa 6 January 2009 в 13:36
поделиться

9 ответов

Существует функциональное различие. Сдвиг изменяет @_, и присвоение от @_ не делает. Если Вы не должны использовать @_ позже, то различие, вероятно, не имеет значения для Вас. Я пытаюсь всегда использовать присвоение списка, но я иногда использую shift.

Однако, если я начинаюсь с shift, как так:

 my( $param ) = shift;

Я часто создаю эту ошибку:

 my( $param, $other_param ) = shift;

Поэтому я не использую shift это часто, таким образом, я забываю преобладать к правой стороне присвоения для изменения этого на @_. В этом суть лучшей практики в не использовании shift. Я мог сделать отдельные строки для каждого shift когда Вы сделали в своем примере, но это просто утомительно.

28
ответ дан 1 December 2019 в 00:01
поделиться

По крайней мере, в моих системах, это, кажется, зависит от версии 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

16
ответ дан 1 December 2019 в 00:01
поделиться

Я предположил бы, что пример сдвига медленнее, чем использование _, потому что это - 6 вызовов функции вместо 1. Примечательно ли это, или даже измеримый другой вопрос. Бросьте каждого в цикл 10k повторений и время их.

Что касается эстетики, я предпочитаю _ метод. Кажется, что было бы слишком легко испортить порядок переменных с помощью метода сдвига с вырезанным и вставленным случайным элементом. Кроме того, я видел, что многие люди делают что-то вроде этого:

sub do_something {
   my $foo = shift;
   $foo .= ".1";

   my $baz = shift;
   $baz .= ".bak";

   my $bar = shift;
   $bar .= ".a";
}

Это, по моему скромному мнению, очень противно и могло легко привести к ошибкам, например, если Вы сокращаете baz блок и вставляете его под блоком панели. Я - все для определения переменных рядом, где они используются, но я думаю, определяя переданный в переменных во главе функции, имеет приоритет.

8
ответ дан 1 December 2019 в 00:01
поделиться

Обычно я использую первую версию. Это вызвано тем, что у меня обычно должна быть проверка ошибок вместе со сдвигами, которую легче записать. Скажите,

sub do_something_fantastical {
    my $foo   = shift || die("must have foo");
    my $bar   = shift || 0;  # $bar is optional
    # ...
}
4
ответ дан 1 December 2019 в 00:01
поделиться

Я предпочитаю распаковывать _ как список (Ваш второй пример). Хотя, как все в Perl, существуют экземпляры, где использование сдвига может быть полезным. Например, методы passthru, которые предназначаются, чтобы быть переопределенными в базовом классе, но Вы хотите удостовериться, что вещи все еще работают, если они не переопределяются.


package My::Base;
use Moose;
sub override_me { shift; return @_; }

3
ответ дан 1 December 2019 в 00:01
поделиться

Я предпочитаю использовать

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. Но этот подход является редко действительно потребностью и должен сохранить меньше стоящим пальцев колледжа прочь.

В заключение удобочитаемость и пригодность для обслуживания, должен особенно в жемчуге, и присвоение _ в одной строке лучше. Если Вы хотите значения по умолчанию набора, можно сделать это сразу после него.

2
ответ дан 1 December 2019 в 00:01
поделиться

Я подозреваю, делаете ли Вы (грубый) эквивалент:

push @bar, shift @_ for (1 :: $big_number);

Затем Вы делаете что-то не так. Я amost всегда использую my ($foo, $bar) = @_; сформируйтесь, потому что я выстрелил себе в ногу с помощью последнего несколько слишком много раз...

0
ответ дан 1 December 2019 в 00:01
поделиться

Наилучший способ, IMHO, представляет собой небольшое смешение этих двух, как в новой функции в модуле:

our $Class;    
sub new {
    my $Class = shift;
    my %opts = @_;
    my $self = \%opts;
    # validate %opts for correctness
    ...
    bless $self, $Class;
}

Затем все вызывающие аргументы в конструктор передаются в виде хэша, что делает код более читаемым чем просто список параметров.

Кроме того, как сказал Брайан , переменная @_ не изменена, что может быть полезно в необычных случаях.

1
ответ дан 1 December 2019 в 00:01
поделиться

Я предпочитаю

sub factorial{
  my($n) = @_;

  ...

}

По той простой причине, что Komodo сможет рассказать мне, каковы аргументы, когда я буду использовать его.

0
ответ дан 1 December 2019 в 00:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: