Perl 为什么不返回数组引用?

Perl 为什么不返回数组引用?,perl,arrays,reference,Perl,Arrays,Reference,在这个问题上,如果没有必要,两个人建议不要进行优化。一般来说,优化会增加复杂性,如果不需要的话,简单会更好。但是在这个特定的例子中,返回数组和数组ref,我看不出有任何增加的复杂性,我认为接口设计的一致性更重要。因此,我几乎总是这样做: sub foo { my($result) = []; #....build up the result array ref $result; } 有什么理由我不应该这样做,即使是很小的结果?我不确定在这种情况下返回引用是否更有效;i、

在这个问题上,如果没有必要,两个人建议不要进行优化。一般来说,优化会增加复杂性,如果不需要的话,简单会更好。但是在这个特定的例子中,返回数组和数组ref,我看不出有任何增加的复杂性,我认为接口设计的一致性更重要。因此,我几乎总是这样做:

sub foo
{
   my($result) = [];

   #....build up the result array ref

   $result;
}

有什么理由我不应该这样做,即使是很小的结果?

我不确定在这种情况下返回引用是否更有效;i、 Perl是否复制子例程返回的数据

通常,如果数组完全在子例程中构造,那么返回引用就没有明显的问题,因为否则数组将被丢弃。但是,如果在返回引用之前还将引用传递到其他地方,则您可能拥有同一引用的两个副本,并且可以在一个地方对其进行修改,但不希望在其他地方进行修改。

否。为清楚起见,请执行“return$result;”操作

我记得曾经测试过它们的效率,对于小型阵列,性能上的差异是最小的。对于大型阵列,返回引用要快得多

这真是一件方便的事,结果很小。您愿意这样做吗:

($foo,$bar) = barbaz();
或返回引用:

 $foobar = barbaz();
 $foobar->[0]; # $foo
 $foobar->[1]; # $bar
返回引用的另一种方法:

($foo,$bar) = @{barbaz()};
作为一项规则,一旦您决定走哪条路,只需为您的模块遵循它,因为从一种方法切换到下一种方法会让人困惑


我通常返回类似事物列表的数组引用,当响应由两到四个不同元素组成时,返回一个数组。除此之外,我还做了一个散列,因为并不是所有调用方都会关心所有响应元素。

当您习惯于将代码用作中的第一个代码段时,您必须将难看的代码作为第二个代码段来编写,或者这不是更好的代码:

my ($foo,$bar) = @{barbaz()};
我认为这是返回引用而不是数组时最大的缺点。如果我想返回少量不同种类的值。我习惯于返回数组并直接赋值给变量(例如在Python中)

如果值的数量更大并且种类繁多,我将使用hash ref返回(更适合重构)


如果返回值是同一类型的,则返回数组或数组引用是有争议的,这取决于数量。

如果数组引用与接口的其余部分不一致,则不应返回数组引用。如果您处理的所有其他内容都返回列表而不是引用,那么不要成为让其他程序员记住异常的怪人

除非您有大的列表,否则这实际上是一个微观优化问题。如果这是你程序中的瓶颈,你应该很幸运

就复杂度而言,引用和列表之间的差异在复杂度上是如此之小,以至于如果您的程序员正在努力解决这个问题,您就会遇到更大的问题。复杂的算法和工作流是复杂的,但这只是语法


话虽如此,我倾向于让所有东西都返回引用,并使接口与之一致。

我认为您不应该只使用一种或两种方法。但是,您应该为每个模块或模块集保持一致

下面是一些值得思考的例子:

sub test1{
  my @arr;
  return @arr;
}
sub test2{
  my @arr;
  return @arr if wantarray;
  return \@arr;
}
sub test3{
  my %hash;
  return %hash;
}
sub test4{
  my %hash;
  return %hash if wantarray;
  return \%hash;
}
sub test5{
  my %hash;
  return $hash{ qw'one two three' } if wantarray;
  return \%hash;
}
{
  package test;
  use Devel::Caller qw'called_as_method';
  sub test6{
    my $out;
    if( wantarray ){
      $out = 'list';
    }else{
      $out = 'scalar';
    }
    $out = "call in $out context";
    if( called_as_method ){
      $out = "method $out";
    }else{
      $out = "simple function $out";
    }
    return $out;
  }
}

我可以看到在未来的项目中可能会使用其中的许多方法,但其中一些方法是毫无意义的。

返回数组会带来一些好处:

my @foo = get_array(); # Get list and assign to array.
my $foo = get_array(); # Get magnitude of list.
my ($f1, $f2) = get_array(); # Get first two members of list.
my ($f3,$f6) = (get_array())[3,6]; # Get specific members of the list.

sub get_array {
   my @array = 0..9;

   return @array;
}
如果返回数组引用,则必须编写几个sub来完成相同的工作。此外,在布尔上下文中,空数组返回false,但空数组ref不返回false

if ( get_array() ) {
    do_stuff();
}
如果返回数组引用,则必须执行以下操作:

if ( @{ get_array_ref() } ) {
    do_stuff();
}
除非get_array_ref()无法返回ref,比如说和undef值,否则程序会停止崩溃。以下其中一项将有所帮助:

if ( @{ get_array() || [] } ) {
    do_stuff();
}

if ( eval{ @{get_array()} } ) {
    do_stuff();
}
因此,如果需要速度优势,或者需要数组引用(也许您希望允许直接操作对象的集合元素——糟糕,但有时必须这样做),请返回数组引用。否则,我认为标准数组的优势值得保留

更新:记住从例程返回的内容并不总是数组或列表,这一点非常重要。返回的内容是
返回之后的内容,或者是上一次操作的结果。您的返回值将在上下文中进行评估。大多数时候,一切都会很好,但有时你会有意想不到的行为

sub foo {
    return $_[0]..$_[1];
}

my $a = foo(9,20);
my @a = foo(9,20);

print "$a\n";
print "@a\n";
与之相比:

sub foo {
    my @foo = ($_[0]..$_[1]);
    return @foo;
}

my $a = foo(9,20);
my @a = foo(9,20);

print "$a\n";
print "@a\n";
所以,当你说“返回数组”时,一定要确定你的意思是“返回数组”。注意你从日常生活中得到的回报。

有什么理由我不这样做,即使是为了小结果


没有特定于perl的原因,这意味着返回对本地数组的引用是正确和高效的。唯一的缺点是,调用函数的人必须处理返回的数组ref,并使用箭头
->
或解引用等方式访问元素。因此,对调用方来说,这稍微有点麻烦

如果数组是在函数内部构造的,则没有理由返回数组;只需返回一个引用,因为调用者保证只有一个副本(它是刚刚创建的)

如果函数正在考虑一组全局数组并返回其中一个,那么如果调用方不修改引用,则可以返回引用。如果调用方可能修改数组,而这不是所需的,那么函数应该返回一个副本

这确实是一个独特的Perl问题。在Java中,您总是返回一个引用,函数通过终结数组及其包含的数据来防止数组被修改(如果这是您的目标的话)。在python中,引用被返回,并且没有办法阻止它们被修改;如果这很重要,则会在inst中返回对副本的引用
sub foo {
    return $_[0]..$_[1];
}

my $a = foo(9,20);
my @a = foo(9,20);

print "$a\n";
print "@a\n";
sub foo {
    my @foo = ($_[0]..$_[1]);
    return @foo;
}

my $a = foo(9,20);
my @a = foo(9,20);

print "$a\n";
print "@a\n";
my ( $name, $number ) = $obj->get_arrayref()->items( 0, 1 );
my ( $name, $number ) = @{ $obj->get_arrayref() };
sub ARRAY::slice { 
    my $arr_ref = shift;
    my $length  = @$arr_ref;
    my @subs    = map { abs($_) < $length ? $_ : $_ < 0 ? 0 : $#$arr_ref } @_;
    given ( scalar @subs ) { 
        when ( 0 ) { return $arr_ref; }
        when ( 2 ) { return [ @{$arr_ref}[ $subs[0]..$subs[1] ] ]; }
        default    { return [ @{$arr_ref}[ @subs ] ]; }
    }
    return $arr_ref; # should not get here.
}

sub ARRAY::items { return @{ &ARRAY::slice }; }
for my $info (@{ getInfo($some, $args) }) {
    ...
}
for my $info ( getInfo($some, $args) ) {
    ...
}
my @info = grep { ... } getInfo($some, $args);
my $address = getInfo($some, $args)->[2];
my $address = (getInfo($some, $args))[2];
my @info = getInfo($some, $args);
my $address = $info[2];
my $address = getInfo($some, $args)->{address};
for my $key (keys %{some_func_that_returns_a_hash_ref}) {
    ...
}
use Method::Signatures;

method foo(\@args) {
    print "@args";      # @args is not a copy
    push @args, 42;   # this alters the caller array
}

my @nums = (1,2,3);
Class->foo(\@nums);   # prints 1 2 3
print "@nums";        # prints 1 2 3 42
package MyClass;

sub new {
  my($class) = @_;
  bless { _things => [] } => $class;
}

sub add_things {
  my $self = shift;
  push @{ $self->{_things} } => @_;
}

sub things {
  my($self) = @_;
  $self->{_things};  # NO!
}
my $obj = MyClass->new;
$obj->add_things(1 .. 3);

...;

my $things = $obj->things;
my $first = shift @$things;
sub things {
  my($self) = @_;
  @{ $self->{_things} };
}
sub get_things {
    my @things;
    ... # populate things
    return wantarray ? @things : \@things;
}
for my $thing ( get_things() ) {
    ...
}
my @things = get_things();
my $things = get_things();