您可以使用引用或传递整个哈希或数组。你的选择。有两个问题可能会让您选择一个而不是另一个:
- 传递其他参数
- 内存管理
Perl 并没有真正的子程序参数。相反,您只是传入一个参数数组。如果您的子程序是查看哪个数组有更多元素怎么办。我不能这样做:
foo(@first, @second);
因为我将传入的是一个结合了两者所有成员的大数组。哈希也是如此。想象一个程序需要两个散列并找到具有公共键的散列:
@common_keys = common(%hash1, %hash1);
同样,我将两个散列中的所有键及其值组合成一个大数组。
解决此问题的唯一方法是传递参考:
foo(\@first, \@second);
@common_keys = common(\%hash1, \%hash2);
在这种情况下,我将传递这两个哈希存储在内存中的内存位置。我的子程序可以使用那些哈希引用。但是,您必须小心,我将在第二个解释中进行解释。
传递引用的第二个原因是内存管理。如果我的数组或哈希是几十个条目,那真的没那么重要。但是,假设我的哈希或数组中有 10,000,000 个条目。复制所有这些成员可能需要相当多的时间。通过引用传递可以节省我的记忆,但代价是可怕的。大多数时候,我使用子程序作为不影响我的主程序的一种方式。这就是为什么假设子程序使用它们自己的变量以及为什么在大多数编程课程中都教你关于变量范围的原因。
但是,当我通过参考时,我打破了这个范围。这是一个不传递引用的简单程序。
#! /usr/bin/env perl
use strict;
use warnings;
my @array = qw(this that the other);
foo (@array);
print join ( ":", @array ) . "\n";
sub foo {
my @foo_array = @_;
$foo_array[1] = "FOO";
}
请注意,子例程foo
1正在更改传入数组的第二个元素。但是,即使我传入@array
,foo
子例程也不会更改 的值@array
。这是因为子例程正在处理一个副本(由 创建my @foo_array = @_;
)。一旦子程序存在,副本就会消失。
当我执行这个程序时,我得到:
this:that:the:other
现在,这是同一个程序,除了我传入了一个引用,并且为了内存管理,我使用了那个引用:
#! /usr/bin/env perl
use strict;
use warnings;
my @array = qw(this that the other);
foo (\@array);
print join ( ":", @array ) . "\n";
sub foo {
my $foo_array_ref = shift;
$foo_array_ref->[1] = "FOO";
}
当我执行这个程序时,我得到:
this:FOO:the:other
那是因为我没有传入数组,而是对该数组的引用。它与保存的内存位置相同@array
。因此,在我的子程序中更改引用会导致它在我的主程序中被更改。大多数时候,您不想这样做。
您可以通过传入一个引用,然后将该引用复制到一个数组来解决这个问题。例如,如果我这样做了:
sub foo {
my @foo_array = @{ shift() };
我将复制我对另一个数组的引用。它保护了我的变量,但这确实意味着我将我的数组复制到另一个需要时间和内存的对象。回到 1980 年代,当我刚开始编程时,这是一个大问题。然而,在这个千兆字节内存和四核处理器的时代,主要问题不是内存管理,而是可维护性。即使您的数组或哈希包含 1000 万个条目,您也可能不会注意到任何时间或内存问题。
这也适用于相反的方式。我可以从我的子例程返回对哈希或整个哈希的引用。许多人喜欢返回参考,但这可能是有问题的。
在面向对象的 Perl 编程中,我使用引用来跟踪我的对象。通常,我会引用一个哈希值,我可以用它来存储其他值、数组和哈希值。
在最近的一个程序中,我计算了 ID 以及它们在日志文件中被引用的次数。这存储在一个对象中(这只是对哈希的引用)。我有一个方法可以返回 ID 的整个散列及其计数。我可以这样做:
return $self->{COUNT_HASH};
但是,如果用户开始修改我传递的那个引用,会发生什么?他们实际上会在不使用我的方法来添加和减去 ID 的情况下操作我的对象。不是我想让他们做的事情。相反,我创建了一个新的散列,然后返回对该散列的引用:
my %hash_counts = % { $self-{COUNT_HASH} };
return \%hash_count;
这复制了我对数组的引用,然后我将引用传递给了数组。这可以保护我的数据免受外部操纵。我仍然可以返回一个引用,但是如果不通过我的方法,用户将无法再访问我的对象。
顺便说一句,我喜欢使用wantarray
which 让调用者可以选择他们想要的数据:
my %hash_counts = %{ $self->{COUNT_HASH} };
return want array ? %hash_counts : \%hash_counts;
这允许我根据用户调用我的对象的方式返回引用或哈希:
my %hash_counts = $object->totals(); # Returns a hash
my $hash_counts_ref = $object->totals(); # Returns a reference to a hash
1脚注:@_
数组指向与调用子程序的参数相同的内存位置。因此,如果我传入foo(@array)
然后$_[1] = "foo";
执行,我将更改@array
.