16

我经常在 Perl 中有一个用一些信息填充数组的子例程。由于我也习惯于使用 C++ 进行黑客攻击,因此我发现自己经常在 Perl 中这样做,使用引用:

my @array;
getInfo(\@array);

sub getInfo {
   my ($arrayRef) = @_;
   push @$arrayRef, "obama";
   # ...
}

而不是更直接的版本:

my @array = getInfo();

sub getInfo {
   my @array;
   push @array, "obama";
   # ...
   return @array;
}

当然,原因是我不希望在子例程中本地创建数组,然后在返回时复制。

是对的吗?或者 Perl 会优化它吗?

4

8 回答 8

18

首先返回一个数组引用怎么样?

sub getInfo {
  my $array_ref = [];
  push @$array_ref, 'foo';
  # ...
  return $array_ref;
}

my $a_ref = getInfo();
# or if you want the array expanded
my @array = @{getInfo()};

根据德曼的评论编辑:

也可以在函数中使用普通数组并返回对它的引用。

sub getInfo {
  my @array;
  push @array, 'foo';
  # ...
  return \@array;
}      
于 2009-02-13T14:55:32.220 回答
13

传递引用效率更高,但差异没有 C++ 中那么大。参数值本身(这意味着:数组中的值)总是通过引用传递(尽管返回的值被复制)。

问题是:重要吗?大多数时候,它没有。如果您要返回 5 个元素,请不要担心。如果您要返回/传递 100'000 个元素,请使用引用。只有当它是一个瓶颈时才优化它。

于 2009-02-13T15:11:27.470 回答
8

如果我查看您的示例并考虑您想要做什么,我习惯于以这种方式编写它:

sub getInfo {
  my @array;
  push @array, 'obama';
  # ...
  return \@array;
}

当我需要返回大量数据时,这对我来说似乎是一个简单的版本。不需要像您在第一个代码片段中编写的那样在外部分配数组,因为它是为您完成的。无论如何,您不应该像Leon Timmermans建议的那样进行过早的优化。submy

于 2009-02-13T15:45:37.653 回答
4

为了回答最后的思考,不,Perl 并没有优化这一点。它不能,真的,因为返回一个数组和返回一个标量是根本不同的。

如果您正在处理大量数据,或者如果性能是一个主要问题,那么您的 C 习惯将很好地为您服务 - 传递和返回对数据结构的引用而不是结构本身,这样它们就不需要被复制。但是,正如 Leon Timmermans 所指出的,在绝大多数情况下,您处理的数据量较小,性能并不是什么大问题,因此请以任何看起来最易读的方式进行。

于 2009-02-13T15:38:35.390 回答
2

这是我通常返回数组的方式。

sub getInfo {
  my @array;
  push @array, 'foo';
  # ...
  return @array if wantarray;
  return \@array;
}

这样,它将以您想要的方式在标量或列表上下文中工作。

my $array = getInfo;
my @array = getInfo;

$array->[0] == $array[0];

# same length
@$array == @array;

我不会尝试优化它,除非你知道它是你代码的一个缓慢部分。即使这样,我也会使用基准测试来查看哪个子程序实际上更快。

于 2009-02-13T17:38:20.720 回答
2

有两个考虑。显而易见的是,您的阵列会有多大?如果它少于几十个元素,那么大小就不是一个因素(除非你正在为一些快速调用的函数进行微优化,但你必须先做一些内存分析来证明这一点)。

那是容易的部分。经常被忽视的第二个考虑因素是界面。返回的数组将如何使用?这很重要,因为在 Perl 中整个数组解引用有点糟糕。例如:

for my $info (@{ getInfo($some, $args) }) {
    ...
}

太丑了 这要好得多。

for my $info ( getInfo($some, $args) ) {
    ...
}

它还适用于映射和 grepping。

my @info = grep { ... } getInfo($some, $args);

但是,如果您要挑选单个元素,则返回数组 ref 可能会很方便:

my $address = getInfo($some, $args)->[2];

这比:

my $address = (getInfo($some, $args))[2];

或者:

my @info = getInfo($some, $args);
my $address = $info[2];

但到那时,您应该质疑@info 是否真的是一个列表或哈希。

my $address = getInfo($some, $args)->{address};

您不应该做的是getInfo()在标量上下文中返回一个数组 ref 并在列表上下文中返回一个数组。这混淆了标量上下文作为数组长度的传统使用,这会让用户感到惊讶。

最后,我将插入我自己的模块Method::Signatures,因为它为传入数组引用提供了一种折衷方案,而无需使用数组引用语法。

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

这是通过Data::Alias的魔力完成的。

于 2009-02-14T00:18:55.640 回答
0

如果您正在读取整个较大的文件并将其切成数组,则还有 3 项潜在的大型性能改进:

  1. 使用 sysread() 而不是 read() 关闭 BUFFERING(手动警告混合)
  2. 通过评估最后一个元素来预扩展数组 - 节省内存分配
  3. 使用 Unpack() 快速拆分 uint16_t 图形通道数据等数据

将数组 ref 传递给函数允许主程序处理一个简单的数组,而一次写入后忘记的工作函数使用更复杂的“$@”和箭头 ->[$II] 访问形式。非常C'ish,它可能很快!

于 2016-03-25T05:57:42.343 回答
-4

我对 Perl 一无所知,所以这是一个语言中立的答案。

从某种意义上说,将数组从子程序复制到调用程序中是低效的。效率低下的原因在于使用的额外内存以及将数据从一个地方复制到另一个地方所花费的时间。另一方面,对于除了最大的数组之外的所有数组,您可能都不在乎,并且可能出于优雅、诅咒或任何其他原因而更喜欢将数组复制出来。

有效的解决方案是子程序将数组的地址传递给调用程序。正如我所说,我对 Perl 在这方面的默认行为一无所知。但是有些语言为程序员提供了选择哪种方法的选项。

于 2009-02-13T15:00:16.000 回答