将@_转换为Perl中的参数传递有什么好处?

时间:2022-06-05 12:15:28
sub foo {
 $arg1 = shift @_;
 $arg2 = shift @_;
 # ...
}

What's the benefit of this idiom? I see only disadvantages compared to explicitly working with $_[0], $_[1], ... The array has to be shifted, which is time consuming. It is destroyed, so that at a later point in time, the arguments have vanished (sad if you need them again and have overwritten your $arg1 with a different value).

这个成语有什么好处?与明确使用$ _ [0],$ _ [1],...相比,我只看到了缺点。数组必须移位,这很费时间。它被破坏了,所以在稍后的时间点,参数已经消失了(如果你再次需要它们并且用不同的值覆盖你的$ arg1,那就很难过)。

4 个解决方案

#1


4  

I agree with all points. But the basic question remains: Why should I shift instead of doing a list assignment or a sequence of scalar assignments?

我同意所有观点。但基本问题仍然存在:我为什么要转移而不是进行列表赋值或一系列标量赋值?

Since I do shifting, I'll explain why I do it.

因为我做了转移,我会解释为什么我这样做。

There are three may ways you can handle parameters to a subroutine:

有三种方法可以处理子程序的参数:

Method 1: List Assignment

 sub foo {
    my ( $user, $date, $system ) = @_;   # No shifting, and @_ preserved

Method 2: Individual Assignment from @_

 sub foo {
    my $user   = $_[1];  
    my $date   = $_[2];   # Must be YYYY-MM-DD
    my $system = $_[3];   # Optional

Method 3: Using "shift"

sub foo {
    my $user    = shift;
    my $date    = shift;    # Must be YYYY-MM-DD
    my $system  = shift;    # Optional

Method 1 is popular. Ikegami uses it, and so do many other advanced Perl developers. It gives you a parameter list and gives you a way of saying these are my parameters and no others.

方法1很受欢迎。 Ikegami使用它,许多其他高级Perl开发人员也是如此。它为您提供了一个参数列表,并为您提供了一种说明这些是我的参数而不是其他参数的方法。

However, Method 2 and Method 3 give you a nice, easy to read parameter list, and you can add comments to the end of the line. However, Method 2 also has the advantage of maintaining the value of @_, so why use shift?

但是,方法2和方法3为您提供了一个简单易读的参数列表,您可以在行尾添加注释。但是,方法2还具有保持@_值的优点,那么为什么要使用shift?

Take a look at Method 2 again. See a problem? I started with @_[1] and not with @_[0] -- a common error. Plus, as you're developing your subroutine, you might decide to reorder your parameters. Using Method 2 means renumbering them. Method 3 doesn't require renumbering, so you don't end up with this:

再看看方法2。看到一个问题?我从@_ [1]开始而不是@_ [0] - 一个常见的错误。另外,在开发子程序时,您可能决定重新排序参数。使用方法2意味着重新编号。方法3不需要重新编号,所以你最终不会这样:

 sub foo {
    my $date   = $_[1];   # Must be YYYY-MM-DD
    my $user   = $_[0];  
    my $system = $_[2];   # Optional

Um, what's the order of the parameters again?

嗯,参数的顺序又是什么?

So, what about preserving the value of @_? If you write a program where you change the value of a parameter, you're probably aren't going to be restoring it from @_. And, if you do, you are likely to produce some error prone code. If you need to munge a parameter, put it in another variable and munge that. This isn't 1975 where computers had only 4 kilobytes of memory.

那么,保留@_的价值呢?如果你编写一个程序来更改参数的值,你可能不会从@_恢复它。而且,如果你这样做,你可能会产生一些容易出错的代码。如果你需要munge一个参数,把它放在另一个变量中,然后进行操作。这不是1975年计算机只有4千字节的内存。

And, talking about the 1970s, computers are fast enough that the amount of time of a few (dozen, hundred, thousands) shift operations isn't going to make that much difference in your total runtime. If your program is inefficient, optimize it where it is inefficient, and not by possibly shaving a few milliseconds eliminating a shift. More time will be spent maintaining your program than actually running it.

而且,谈到20世纪70年代,计算机足够快,以至于几个(十二,一百,几千)个班次操作的时间量不会在总运行时间上产生那么大的差异。如果您的程序效率低下,请在低效率的地方进行优化,而不是在几毫秒内消除转换。维护程序所花费的时间比实际运行时间要长。


Damian Conway (The author of Perl Best Practices) recommends both Method #1 and Method #3. He states that Method #1 is "...is more concise, and it keeps the parameters together in a horizontal list, which enhances readability, provided that the number of parameters is small." (the emphasis is mine).

Damian Conway(Perl Best Practices的作者)推荐方法#1和方法#3。他声称方法#1是“......更简洁,它将参数保存在水平列表中,只要参数数量很少,就可以提高可读性。” (重点是我的)。

Damian states about Method #3: "The shift-based version is preferable, though, whenever one or more arguments has to be sanity-checked or needs to be documented with a trailing comment."

Damian对方法#3说:“无论何时,只要一个或多个参数必须经过健全检查或需要记录一个尾随注释,基于班次的版本更可取。”

I simply use Method #3 all of the time. This way, I don't have to worry about reformatting if my parameter list grows, and I simply think it looks better.

我只是一直使用方法#3。这样,如果我的参数列表增长,我不必担心重新格式化,我只是认为它看起来更好。

#2


9  

Shifting @_ is common in OO perl so parameters can be separated from instance of the class which is automatically added as first element of @_.

移位@_在OO perl中很常见,因此参数可以与类的实例分开,该类自动添加为@_的第一个元素。

It can also be used to write less when assigning default values for input parameters, although personally I don't find it appealing,

在为输入参数指定默认值时,它也可以用来少写,虽然我个人觉得它不吸引人,

sub foo {
  my $arg1 = shift // 2;
  my $arg2 = shift // 7;
  # ...
}

I see only disadvantages compared to explicitly working with $[0], $1, ...

与明确使用$ [0],$ 1,...相比,我认为只有缺点

Instead of working with $_[0], $_[1], assigning whole @_ array at once is better/less error prone/more readable practice.

而不是使用$ _ [0],$ _ [1],一次分配整个@_数组更好/更少错误/更可读的做法。

my ($arg1, $arg2) = @_;

Also note that @_ elements are aliased to passed variables, so accidental changes could happen,

另请注意,@ _元素是通过变量的别名,因此可能会发生意外更改,

sub foo {
  $_[0] = 33;
  $_[1] = 44;
}

my ($x, $y) = (11,22);

print "($x, $y)\n";
foo($x, $y);
print "($x, $y)\n";

output

(11, 22)
(33, 44)

#3


5  

It's faster to access $_[0], etc directly than to use named parameters[1], but there are a number of good reasons to prefer named parameters.

直接访问$ _ [0]等比使用命名参数[1]更快,但是有很多理由更喜欢命名参数。

  1. By far the most important reason, using named parameters serves as a form of documentation.

    到目前为止,使用命名参数的最重要原因是作为一种文档形式。

  2. Using @_ directly is hard to read because it's hard to keep track of which argument has what value.

    直接使用@_很难阅读,因为很难跟踪哪个参数具有什么价值。

    For example, "What does $_[4] contain? Is it the rows? Or is that $_[5]?"

    例如,“$ _ [4]包含什么?是行吗?还是那个$ _ [5]?”

  3. It's easy to use the wrong index by accident.

    偶然使用错误的索引很容易。

    • It's easy to type the wrong number.
    • 键入错误的数字很容易。

    • It's easy to think you want one number when you want another.
    • 当你想要另一个号码时,很容易想到你想要一个号码。

    • It's easy to overlook an index when changing a sub's parameters.
    • 更改子参数时很容易忽略索引。

  4. Using @_ directly is hard to read because of the amount of symbols.

    由于符号的数量,直接使用@_很难阅读。

    Compare

    grep { $_[0]{$_} =~ /.../ } keys %{$_[0]}
    

    with

    grep { $foos->{$_} =~ /.../ } keys %$foos
    
  5. It's useful for providing defaults.

    它对提供默认值很有用。

    sub f {
       my $x = shift // "abc";
       my $y = shift // "def";
       ...
    }
    
  6. Copying into a scalar effectively introduces pass-by-copy semantics, which can be useful.

    复制到标量有效地引入了逐个拷贝的语义,这可能很有用。

    $ perl -le'my $x=123; sub f {                  $x=456; print $_[0]; } f($x);'
    456
    
    $ perl -le'my $x=123; sub f { my $arg = shift; $x=456; print $arg;  } f($x);'
    123
    

Notes:

  1. Whether it's my preferred

    是否是我的首选

    sub f {
       my (...) = @_;
       ...
    }
    

    or

    sub f {
        my ... = shift; 
        my ... = shift;
        ...
    }
    

#4


0  

When writing methods in object classes, I always shift the invocant off @_ first, but then just unpack the remaining arguments out, thus preserving all the other non-invocant arguments. This allows the call sites to look more like the method definition itself, in terms of what's inside the parens.

在对象类中编写方法时,我总是首先将调用者移出@_,然后将其余的参数解包出来,从而保留所有其他非调用的参数。这允许调用站点看起来更像是方法定义本身,就像parens里面的内容而言。

sub method
{
  my $self = shift;
  my ( $x, $y, $z ) = @_;
}

$obj->method( "x", "y", "z" );

This way if I have to delegate the method call elsewhere, I can simply pass @_ itself:

这样,如果我必须将方法调用委托给别处,我可以简单地传递@_本身:

sub do_thing_with_whatsit
{
  my $self = shift;
  $self->{whatsit}->do_thing( @_ );
}

#1


4  

I agree with all points. But the basic question remains: Why should I shift instead of doing a list assignment or a sequence of scalar assignments?

我同意所有观点。但基本问题仍然存在:我为什么要转移而不是进行列表赋值或一系列标量赋值?

Since I do shifting, I'll explain why I do it.

因为我做了转移,我会解释为什么我这样做。

There are three may ways you can handle parameters to a subroutine:

有三种方法可以处理子程序的参数:

Method 1: List Assignment

 sub foo {
    my ( $user, $date, $system ) = @_;   # No shifting, and @_ preserved

Method 2: Individual Assignment from @_

 sub foo {
    my $user   = $_[1];  
    my $date   = $_[2];   # Must be YYYY-MM-DD
    my $system = $_[3];   # Optional

Method 3: Using "shift"

sub foo {
    my $user    = shift;
    my $date    = shift;    # Must be YYYY-MM-DD
    my $system  = shift;    # Optional

Method 1 is popular. Ikegami uses it, and so do many other advanced Perl developers. It gives you a parameter list and gives you a way of saying these are my parameters and no others.

方法1很受欢迎。 Ikegami使用它,许多其他高级Perl开发人员也是如此。它为您提供了一个参数列表,并为您提供了一种说明这些是我的参数而不是其他参数的方法。

However, Method 2 and Method 3 give you a nice, easy to read parameter list, and you can add comments to the end of the line. However, Method 2 also has the advantage of maintaining the value of @_, so why use shift?

但是,方法2和方法3为您提供了一个简单易读的参数列表,您可以在行尾添加注释。但是,方法2还具有保持@_值的优点,那么为什么要使用shift?

Take a look at Method 2 again. See a problem? I started with @_[1] and not with @_[0] -- a common error. Plus, as you're developing your subroutine, you might decide to reorder your parameters. Using Method 2 means renumbering them. Method 3 doesn't require renumbering, so you don't end up with this:

再看看方法2。看到一个问题?我从@_ [1]开始而不是@_ [0] - 一个常见的错误。另外,在开发子程序时,您可能决定重新排序参数。使用方法2意味着重新编号。方法3不需要重新编号,所以你最终不会这样:

 sub foo {
    my $date   = $_[1];   # Must be YYYY-MM-DD
    my $user   = $_[0];  
    my $system = $_[2];   # Optional

Um, what's the order of the parameters again?

嗯,参数的顺序又是什么?

So, what about preserving the value of @_? If you write a program where you change the value of a parameter, you're probably aren't going to be restoring it from @_. And, if you do, you are likely to produce some error prone code. If you need to munge a parameter, put it in another variable and munge that. This isn't 1975 where computers had only 4 kilobytes of memory.

那么,保留@_的价值呢?如果你编写一个程序来更改参数的值,你可能不会从@_恢复它。而且,如果你这样做,你可能会产生一些容易出错的代码。如果你需要munge一个参数,把它放在另一个变量中,然后进行操作。这不是1975年计算机只有4千字节的内存。

And, talking about the 1970s, computers are fast enough that the amount of time of a few (dozen, hundred, thousands) shift operations isn't going to make that much difference in your total runtime. If your program is inefficient, optimize it where it is inefficient, and not by possibly shaving a few milliseconds eliminating a shift. More time will be spent maintaining your program than actually running it.

而且,谈到20世纪70年代,计算机足够快,以至于几个(十二,一百,几千)个班次操作的时间量不会在总运行时间上产生那么大的差异。如果您的程序效率低下,请在低效率的地方进行优化,而不是在几毫秒内消除转换。维护程序所花费的时间比实际运行时间要长。


Damian Conway (The author of Perl Best Practices) recommends both Method #1 and Method #3. He states that Method #1 is "...is more concise, and it keeps the parameters together in a horizontal list, which enhances readability, provided that the number of parameters is small." (the emphasis is mine).

Damian Conway(Perl Best Practices的作者)推荐方法#1和方法#3。他声称方法#1是“......更简洁,它将参数保存在水平列表中,只要参数数量很少,就可以提高可读性。” (重点是我的)。

Damian states about Method #3: "The shift-based version is preferable, though, whenever one or more arguments has to be sanity-checked or needs to be documented with a trailing comment."

Damian对方法#3说:“无论何时,只要一个或多个参数必须经过健全检查或需要记录一个尾随注释,基于班次的版本更可取。”

I simply use Method #3 all of the time. This way, I don't have to worry about reformatting if my parameter list grows, and I simply think it looks better.

我只是一直使用方法#3。这样,如果我的参数列表增长,我不必担心重新格式化,我只是认为它看起来更好。

#2


9  

Shifting @_ is common in OO perl so parameters can be separated from instance of the class which is automatically added as first element of @_.

移位@_在OO perl中很常见,因此参数可以与类的实例分开,该类自动添加为@_的第一个元素。

It can also be used to write less when assigning default values for input parameters, although personally I don't find it appealing,

在为输入参数指定默认值时,它也可以用来少写,虽然我个人觉得它不吸引人,

sub foo {
  my $arg1 = shift // 2;
  my $arg2 = shift // 7;
  # ...
}

I see only disadvantages compared to explicitly working with $[0], $1, ...

与明确使用$ [0],$ 1,...相比,我认为只有缺点

Instead of working with $_[0], $_[1], assigning whole @_ array at once is better/less error prone/more readable practice.

而不是使用$ _ [0],$ _ [1],一次分配整个@_数组更好/更少错误/更可读的做法。

my ($arg1, $arg2) = @_;

Also note that @_ elements are aliased to passed variables, so accidental changes could happen,

另请注意,@ _元素是通过变量的别名,因此可能会发生意外更改,

sub foo {
  $_[0] = 33;
  $_[1] = 44;
}

my ($x, $y) = (11,22);

print "($x, $y)\n";
foo($x, $y);
print "($x, $y)\n";

output

(11, 22)
(33, 44)

#3


5  

It's faster to access $_[0], etc directly than to use named parameters[1], but there are a number of good reasons to prefer named parameters.

直接访问$ _ [0]等比使用命名参数[1]更快,但是有很多理由更喜欢命名参数。

  1. By far the most important reason, using named parameters serves as a form of documentation.

    到目前为止,使用命名参数的最重要原因是作为一种文档形式。

  2. Using @_ directly is hard to read because it's hard to keep track of which argument has what value.

    直接使用@_很难阅读,因为很难跟踪哪个参数具有什么价值。

    For example, "What does $_[4] contain? Is it the rows? Or is that $_[5]?"

    例如,“$ _ [4]包含什么?是行吗?还是那个$ _ [5]?”

  3. It's easy to use the wrong index by accident.

    偶然使用错误的索引很容易。

    • It's easy to type the wrong number.
    • 键入错误的数字很容易。

    • It's easy to think you want one number when you want another.
    • 当你想要另一个号码时,很容易想到你想要一个号码。

    • It's easy to overlook an index when changing a sub's parameters.
    • 更改子参数时很容易忽略索引。

  4. Using @_ directly is hard to read because of the amount of symbols.

    由于符号的数量,直接使用@_很难阅读。

    Compare

    grep { $_[0]{$_} =~ /.../ } keys %{$_[0]}
    

    with

    grep { $foos->{$_} =~ /.../ } keys %$foos
    
  5. It's useful for providing defaults.

    它对提供默认值很有用。

    sub f {
       my $x = shift // "abc";
       my $y = shift // "def";
       ...
    }
    
  6. Copying into a scalar effectively introduces pass-by-copy semantics, which can be useful.

    复制到标量有效地引入了逐个拷贝的语义,这可能很有用。

    $ perl -le'my $x=123; sub f {                  $x=456; print $_[0]; } f($x);'
    456
    
    $ perl -le'my $x=123; sub f { my $arg = shift; $x=456; print $arg;  } f($x);'
    123
    

Notes:

  1. Whether it's my preferred

    是否是我的首选

    sub f {
       my (...) = @_;
       ...
    }
    

    or

    sub f {
        my ... = shift; 
        my ... = shift;
        ...
    }
    

#4


0  

When writing methods in object classes, I always shift the invocant off @_ first, but then just unpack the remaining arguments out, thus preserving all the other non-invocant arguments. This allows the call sites to look more like the method definition itself, in terms of what's inside the parens.

在对象类中编写方法时,我总是首先将调用者移出@_,然后将其余的参数解包出来,从而保留所有其他非调用的参数。这允许调用站点看起来更像是方法定义本身,就像parens里面的内容而言。

sub method
{
  my $self = shift;
  my ( $x, $y, $z ) = @_;
}

$obj->method( "x", "y", "z" );

This way if I have to delegate the method call elsewhere, I can simply pass @_ itself:

这样,如果我必须将方法调用委托给别处,我可以简单地传递@_本身:

sub do_thing_with_whatsit
{
  my $self = shift;
  $self->{whatsit}->do_thing( @_ );
}