【问题标题】:When is it better to use an array instead of a hash in Perl?什么时候在 Perl 中使用数组而不是散列更好?
【发布时间】:2014-10-08 10:27:44
【问题描述】:

假设你有一个数组@a = qw/ a b c d/;

还有一个哈希%a = ('a' => 1, 'b' => 1, 'c' => 1, 'd' => 1);

是否存在创建数组版本比创建哈希更好的情况(除非您必须像在类似的东西中那样遍历所有值

for (@a){
    ....

在这种情况下,如果您使用散列,则必须使用keys %a)?因为测试特定值是否在哈希中总是比在数组中更有效,对吗?

【问题讨论】:

  • 哈希键没有预测顺序,所以它与数组有很大的不同。
  • 如果这个问题被关闭,那就太可惜了。我认为这是一个很好的问题,它的答案可以帮助很多人。
  • @ThisSuitIsBlackNot,它不是链接问题的副本。链接的问题询问$x{$i} 是否真的比$x[$i] 快。 (不是。)这与泰勒的问题无关。
  • 在某些情况下应该使用哈希的另一个原因stackoverflow.com/questions/54833426/…

标签: arrays perl hash


【解决方案1】:
    • 数组按数字索引。
    • 哈希以字符串为键。
    • 直到最高索引的所有索引都存在于一个数组中。
    • 散列索引稀疏。 (例如,“a”和“c”可以在没有“b”的情况下存在。)

有许多新兴属性。首先,

    • 数组可用于存储有序列表。
    • 以这种方式使用哈希值会很丑陋且效率低下。
    • 不能从数组中删除元素,除非它是索引最高的元素。
    • 您可以从使用数组实现的有序列表中删除,但删除除第一个或最后一个以外的元素效率低下。
    • 可以从哈希中删除元素,而且效率很高。

【讨论】:

    【解决方案2】:

    数组是有序的值列表。它们可以包含重复值。

    @array = qw(a b c a);
    

    哈希是键(必须是唯一的)和值(可以重复)之间的映射。散列(实际上)是无序的,这意味着键以明显随机的顺序出现,而不是它们的输入顺序。

    %hash = (a => 1, b => 2, c => 3);
    

    当只有键很重要时,哈希也可以用作集合。集合是无序的,并且只包含唯一的“值”(散列的键)。

    %set = (a => undef, b => undef, c => undef);
    

    使用哪一个取决于您的数据和算法。当顺序很重要时(特别是如果您无法排序以得出顺序)或可能存在重复值时,请使用数组。当值必须是唯一的并且不关心顺序时,使用集合(即使用散列作为集合)。当唯一性很重要、顺序不重要(或易于排序)并且查找基于任意值而不是整数时,请使用哈希。

    您可以组合数组和哈希(通过引用)来创建任意复杂的数据结构。

    @aoa = ([1, 2, 3], [4, 5, 6]);               # array of arrays ("2D" array)
    %hoh = (a => { x => 1 }, b => { x => 2 });   # hash of hashes
    @aoh = ({a => 1, b => 2}, {a => 3, b => 4}); # array of hashes
    %hoa = (a => [1, 2], b => [3, 4]);           # hash of arrays
    ...etc.
    

    【讨论】:

    • Re “数组是有序的值列表。”不比散列更重要。例如$a[2] = "a"; $a[0] = "b"; $a[1] = "c"; print values(@a); 不会给abc 任何比$h{2} = "a"; $h{0} = "b"; $h{1} = "c"; print values(%h); 更多的东西。这些值只是键控的,您可以对数组和散列的键进行排序。真正的区别在于对数组的键进行排序会更有效。 (在提到 push 和 pop 之前,请记住,您可以很容易地使 push 和 pop 适用于哈希。)
    • @ikegami:当然数组是有序的。您的@a 将始终打印为bca。它们按索引排序。我从来没有说过他们的排序是基于插入。
    • 要么重复我已经说过的内容(您可以对索引进行排序),要么您说哈希也是排序的(按桶索引)。 (不要忘记,散列的核心是一个数组。“散列”这个词来源于这样一个事实,即键被散列为一个数字,该数字用作该数组的索引。)
    • 是的,散列是有序的,但该顺序对程序员是隐藏的,如有更改,恕不另行通知。从 Perl 5.18 开始,哈希顺序是随机的,并且在同一程序的两次执行之间不会相同。这就是为什么我说散列实际上是无序的。
    • 另一方面,数组被定义为在 perldata 中排序:“...数组是标量的有序列表...”。该顺序是有保证的(没有排序)。我有点困惑为什么你似乎在争论。
    【解决方案3】:

    这是关于使用数字作为哈希键的。它没有直接回答问题,因为它没有比较数组提供的设施,但我认为这是放置信息的好地方。

    假设使用这样的代码构建一个包含十个元素的哈希

    use strict;
    use warnings;
    
    my %hash;
    my $n = 1000;
    for (1 .. 10) {
      $hash{$n} = 1;
      $n *= 1000;
    }
    

    然后我们查询它,寻找十次方的键。当然,整数乘以十最简单的方法就是加一个零,这样写就可以了

    my $m = '1';
    
    for (1 .. 100) {
      print $m, "\n" if $hash{$m};
      $m .= 0;
    }
    

    有输出

    1000
    1000000
    1000000000
    1000000000000
    1000000000000000
    1000000000000000000
    

    我们输入了 10 个元素,但这只显示了 6 个。发生了什么?让我们看一下哈希中的内容。

    use Data::Dump;
    dd \%hash;
    

    这个输出

    {
      "1000"                => 1,
      "1000000"             => 1,
      "1000000000"          => 1,
      "1000000000000"       => 1,
      "1000000000000000"    => 1,
      "1000000000000000000" => 1,
      "1e+021"              => 1,
      "1e+024"              => 1,
      "1e+027"              => 1,
      "1e+030"              => 1,
    }
    

    所以哈希没有使用我们想象的键。它以一种尝试模仿的方式将数字串起来是愚蠢的。

    举一个更实际的例子,假设我们有一些圈子,想按区域收集到集合中。显而易见的事情是使用该区域作为哈希键,就像这个程序创建了 100,000 个随机整数直径最大为 1800 万的圆。

    use strict;
    use warnings;
    use 5.010;
    
    package Circle;
    
    use Math::Trig 'pi';
    
    sub new {
      my $class = shift;
      my $self = { radius => shift };
      bless $self, $class;
    }
    
    sub area {
      my $self = shift;
      my $radius = $self->{radius};
      pi * $radius * $radius;
    }
    
    
    
    package main;
    
    my %circles;
    
    for (1 .. 100_000) {
       my $circle = Circle->new(int rand 18_000_000);
       push @{ $circles{$circle->area} }, $circle;
    }
    

    现在让我们看看这些哈希键中有多少使用科学记数法

    say scalar grep /e/, keys %circles;
    

    它说(当然是随机的)

    861
    

    所以如果我们指定一个数字作为散列索引,那么真的没有一个整洁的方法可以知道 string perl 将使用什么。

    【讨论】:

      【解决方案4】:

      在 Perl 中,@array 是由整数(正数和负数)访问的值 ($v1, $v2, ...) 的有序列表, 而 %hash 是由字符串访问的 'key => value' 对 (k1 => $v1, k2 => $v2, ...) 的无序列表。

      CPAN 上有实现有序哈希的模块,例如:Hash::Ordered 和 Tie::IxHash

      当您订购的“项目”可能也很大时,您可能希望使用数组,因为 使用 %hash 并对键和/或值进行排序会效率低下。

      【讨论】:

        猜你喜欢
        • 2015-07-23
        • 2012-06-15
        • 2011-06-26
        • 1970-01-01
        • 1970-01-01
        • 2012-01-15
        • 2013-05-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多