何时返回数组或哈希,何时仅返回引用?

4

我对以下内容感到困惑。
有时候我会看到这样的例子:

my %hash = get_data();

sub get_data {
    my %data = ();
    # do processing
    return %data;
}

而数组也是类似的。
my @arrays = get_data();

sub get_data {
    my @data = ();
    # do processing
    return @data;
}

我最初认为函数只能返回对数组或哈希的引用,而不能返回它们本身。
所以我不明白两者之间的区别以及我们应该何时选择其中一个?
这是否与垃圾回收或数据复制过多有关呢?


3
函数总是返回列表。如果使用return @data,则返回@data元素的列表,如果使用return %data,则返回%data键/值元素的列表,如果使用\%data,则返回一个元素(哈希引用)的列表。您可以将这样的列表输入到哈希表中,就像您所做的那样,也可以输入到数组等其他数据结构中。 - mpapec
@mpapec:那么有什么区别呢?数组只是一个列表对象,不是吗?哈希也可以表示为列表。我觉得我错过了一些重要的东西。代码是否被错误地粘贴了? - Jim
3
数组和哈希表是容器,而您可以将列表视为瞬态或即时结构。链接1链接2 - mpapec
@mpapec:那么OP中的代码没有任何隐藏的错误吗? - Jim
返回引用应该更有效率,但需要使用不同的数组大小进行基准测试。 - mpapec
显示剩余3条评论
2个回答

13
严格来说,你不能从Perl子例程返回数组或哈希。Perl子例程返回列表。列表类似于数组,因为它们是值的序列,但它们并不是数组。数组是变量,而列表是无名、不可变、瞬态的数据结构,用于传递和返回值、初始化数组和哈希等。这是一个 有点微妙 的观点,但也是一个重要的观点。
当你写 return @data 时,你并没有返回 @data 数组;你返回的是它包含的值的列表。同样地,return %data 返回哈希中包含的键/值对的列表。这些值可以用于初始化另一个数组或哈希,这就是你的示例中发生的情况。初始化的数组/哈希包含子例程使用的数组/哈希的 (浅) 复制。
要“返回”一个数组或哈希表,你必须返回对它的引用。例如:return \@datareturn \%data。这样做会返回变量本身的引用。修改它将影响原始数组,因为它们使用的是同一存储空间。
是否应该将子程序作为列表(副本)或引用返回是编程决策。对于总是返回具有位置意义的N个值的子程序(例如内置的localtime),返回列表是有意义的。对于返回任意大的列表的子程序,通常最好返回引用,因为这更有效率。
甚至可以通过使用wantarray来决定子程序如何返回,这让调用者决定他们想要什么。
sub get_data {
    my @data;
    ...
    return wantarray ? @data : \@data;
}

my $aref  = get_data(); # returns a reference
my @array = get_data(); # returns a list

垃圾回收怎么处理?如果我们从一个函数中返回一个引用,只要有人持有这个引用,它就不会被释放(我指的是内存),对吗? - Jim
2
@Jim:正确。通过返回引用,您可以获得一个数组的单个副本,只要至少有一个引用存在,它就会持续存在。返回列表会导致多个数组副本,但子副本立即符合垃圾回收条件。 - Michael Carman

4

你实际上是在创建一个新的数组(或哈希表),其中填充与在子程序中生成的相同的元素:

sub get_data{
    # initialize an array
    my @toReturn = qw/ a b c d e f g /;

    # get its location in memory
    my $toReturn_ref = \@toReturn;

    # print its location in memory
    print "toReturn: $toReturn_ref\n";

    # return the **elements** in the array (not the array itself)
    return @toReturn;
}

# initialize an array
my @arr = get_data();

# get its location in memory
my $arr_ref = \@arr;

# print its location in memory
print "\"Returned\": $arr_ref\n";

这将会打印出类似于下面的内容:
toReturn:   ARRAY(0x1df85e8)
"Returned": ARRAY(0x1debc40)

它们是不同的数组,但恰巧具有相同的内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接