为什么我会在Perl中返回哈希或哈希引用?

时间:2010-01-05 23:06:18

标签: perl hash reference

实现以下目标的最有效方法是什么? (我知道他们完成了同样的事情,但大多数人如何在三者之间做到这一点,为什么?)

档案a.pl

my %hash = build_hash();
# Do stuff with hash using $hash{$key}
sub build_hash
{
    # Build some hash
    my %hash = ();
    my @k = qw(hi bi no th xc ul 8e r);
    for ( @k )
    {
        $hash{$k} = 1;
    }

    # Does this return a copy of the hash??
    return %hash;
}

文件b.pl

my $hashref = build_hash();
# Do stuff with hash using $hashref->{$key}
sub build_hash
{
    # Build some hash
    my %hash = ();
    my @k = qw(hi bi no th xc ul 8e r);
    for ( @k )
    {
        $hash{$k} = 1;
    }

    # Just return a reference (smaller than making a copy?)
    return \%hash;
}

文件c.pl

my %hash = %{build_hash()};
# Do stuff with hash using $hash{$key}
# It is better, because now we don't have to dereference our hashref each time using ->?

sub build_hash
{
    # Build some hash
    my %hash = ();
    my @k = qw(hi bi no th xc ul 8e r);
    for ( @k )
    {
        $hash{$k} = 1;
    }

    return \%hash;
}

8 个答案:

答案 0 :(得分:22)

我更喜欢返回散列引用,原因有两个。一,它使用的内存少一点,因为没有副本。第二,如果您只需要一个哈希值,就可以执行此操作。

my $value = build_hash()->{$key};

学会喜欢哈希引用,一旦你开始使用对象,你将会看到很多。

答案 1 :(得分:9)

为什么不回归?上下文是Perl中一个非常强大的功能,允许您的功能“做你的意思”。通常,决定哪个是更好的返回值取决于调用代码计划如何使用该值,这正是Perl具有内置wantarray的原因。

sub build_hash {
    my %hash;
    @hash{@keys} = (1) x @keys;
    wantarray ? %hash : \%hash
}

my %hash = build_hash;  # list context, a list of (key => value) pairs
my $href = build_hash;  # scalar context, a hash reference

答案 2 :(得分:8)

我会返回引用以保存将散列扁平化为标量列表的处理时间,构建新散列和(可能)垃圾收集子例程中的本地散列。

答案 3 :(得分:5)

您正在寻找的是哈希切片

# assigns the value 1 to every element of the hash

my %hash;                                   # declare an empty hash
my @list = qw(hi bi no th xc ul 8e r);      # declare the keys as a list
@hash{@list} =                              # for every key listed in @list,
                (1) x @list;                # ...assign to it the corresponding value in this list
                                            # which is (1, 1, 1, 1, 1...)  (@list in scalar context
                                            #   gives the number of elements in the list)

x运算符在perldoc perlop描述。

有关数据结构和参考资料的教程,请参阅perldoc perldscperldoc perlreftut(初学者和专家都必须阅读)。 Hash切片本身在perldoc perldata中提到。

关于从函数返回哈希,通常你应该返回哈希本身,而不是引用。如果散列是 huge 并且内存或时间是一个问题,你可以使用引用,但这不应该是你的第一个担心 - 使代码工作。

函数的返回值总是列表(返回标量本质上是一个元素的列表)。散列是Perl中的列表:您可以互换地将一个分配给另一个(假设列表具有偶数个元素,并且没有键冲突会导致某些值在转换过程中丢失):

use strict; use warnings;
use Data::Dumper;

function foo
{
    return qw(key1 value1 key2 value2);
}

my @list = foo();
my %hash = foo();

print Dumper(\@list);
print Dumper(\%hash);

给出:

$VAR1 = [
          'key1',
          'value1',
          'key2',
          'value2'
        ];

$VAR1 = {
          'key2' => 'value2',
          'key1' => 'value1'
        };

PS。我强烈建议你编写像上面这样的小样本程序来玩数据结构并看看会发生什么。你可以通过实验学到很多东西!

答案 4 :(得分:2)

a.plc.pl需要获取哈希的副本(并且函数内部的哈希标记为空闲内存)。另一方面,b.pl只构建一次哈希,并且需要很少的额外内存来返回可以操作的引用。因此b.pl更有可能是空间和时间三者中最有效的形式。

答案 5 :(得分:2)

我要反对其他人说的话,并说我更喜欢将我的数据作为哈希返回(好吧,作为一个偶数大小的列表,可能会被解释为哈希)。我在一个我们倾向于执行以下代码片段之类的环境中工作,并且当您不必每隔一行取消引用时,它更容易组合和排序以及切片和切块。 (也很高兴知道有人不能破坏你的hashref,因为你按值传递了整个东西。编辑:除非你在哈希值中引用了其他对象/哈希/数组,无论如何你都有麻烦了。)

my %filtered_config_slice = 
   hashgrep { $a !~ /^apparent_/ && defined $b } (
   map { $_->build_config_slice(%some_params, some_other => 'param') } 
   ($self->partial_config_strategies, $other_config_strategy)
);

这近似于我的代码可能做的事情:基于各种配置策略对象构建对象的配置(对象本身知道其中一些,加上一些额外的人),然后将其中一些过滤掉为无关。 / p>

(是的,我们有很好的工具,例如hashgrephashmap以及lkeys,可以对哈希做有用的事情。$ a和$ b设置为键和每个键的值列表中的项目,分别)。 (是的,我们有人可以在这个级别进行编程。招聘是令人讨厌的,但我们有一个优质的产品。)

如果您不打算执行类似函数式编程的任何操作,或者您需要更多性能(是否已分析?),请确保使用hashrefs。

答案 6 :(得分:2)

  

关于从a返回哈希   功能,通常你应该返回   哈希本身,而不是引用。您   如果哈希是,则可以使用引用   巨大的记忆或时间是一个问题,   但这不应该是你的第一个担心    - 让代码正常工作。

我将不得不在这里不同意以太。曾经有一段时间我占据了这个位置,但很快发现自己陷入了一个必须记住哪些sub返回哈希并返回hashrefs的地狱,这对于让代码正常工作是一个相当严重的障碍。标准化总是返回哈希/数组或总是返回hashref / arrayref非常重要,除非您想要经常绊倒自己。

至于标准化的内容,我认为参考文献有几个好处:

  • 当您返回散列或数组时,您实际返回的是包含原始散列/数组的展平副本的列表。就像将哈希/数组参数传递给sub一样,这样做的缺点是您一次只能发送一个列表。当然,您通常不需要返回多个值列表,但它确实会发生,那么为什么选择以排除它的方式标准化做事?

  • 返回单个标量而不是可能更大的数据块的(通常可忽略的)性能/内存优势。

  • 它与OO代码保持一致,OO代码经常来回传递对象(即祝福的引用)。

  • 如果出于某种原因,重要的是你拥有散列/数组的新副本而不是对原始数据的引用,那么调用代码可以轻松地创建一个,如{{1}中所示的OP所示}。但是,如果您返回哈希的副本,则调用者无法将其转换为对原始引用的引用。 (如果这是有利的,该函数可以复制并返回对副本的引用,从而保护原件,同时也避免“ this 返回哈希, 返回hashrefs“我之前提到的。”

  • 正如Schwern所说,能够做c.pl真的很棒。

我可以看到始终返回哈希/数组的唯一优势是,对于那些不理解引用或不习惯使用它们的人来说,它更容易。鉴于参考文献的安慰需要花费数周或数月的时间才能完成,随后是数年或数十年的流利工作,我认为这不是一个有意义的好处。

答案 7 :(得分:1)

小心:a.pl返回带有偶数个元素的列表,而不是哈希。然后,当您将这样的列表分配给哈希变量时,将使用偶数索引处的元素作为键并将奇数索引处的元素作为值来构建哈希。 [编辑:这就是我一直看到的问题,但sub { ... %hash }实际上与sub { ... @list }的行为略有不同。 ]

出于同样的原因,以您描述的方式构建哈希就像:

my %hash = map { $_ => 1 } qw(hi bi no th xc ul 8e r);

我个人的经验法则是避免引用,除非我真的需要它们(例如嵌套结构,或者当你真的需要传递对相同的东西的引用时)。

编辑:(我不能再点击“添加评论”链接了?!在这里使用鼠标键......) 我考虑过一点,我认为传递哈希引用可能更好,因为我们使用哈希的方式。上面的段落仍适用于数组引用。

感谢Schwern和Ether的评论。