22

在以下程序中使用哈希计算实际列表的出现后,如何维护实际列表的顺序?例如,<DATA>

a
b
e
a
c 
d 
a
c
d
b
etc.

使用哈希,我计算了每个元素的出现次数。

我想要的是:

a  3
b  2
e  1
c  2
d  2

但以下程序向我展示了其他方式。

my (%count, $line, @array_1, @array_2);
while ($line = <DATA>) {
    $count{$line}++ if ( $line =~ /\S/ );
}
@array_1 = keys(%count);
@array_2 = values(%count);
for(my $i=0; $i<$#array_1; $i++)
{
   print "$array_1[$i]\t $array_2[$i]";
}
4

7 回答 7

36

哈希没有排序,但像往常一样,CPAN 提供了一个解决方案:Tie::IxHash

use Tie::IxHash;
my %count;
tie %count, 'Tie::IxHash';

while ($line = <DATA>) {
$count{$line}++ if ( $line =~ /\S/ );
}

while( my( $key, $value)= each %count) {
    print "$key\t $value"; 
}
于 2009-10-13T08:46:33.063 回答
15

哈希表中的数据是按照键的哈希码的顺序存储的,在大多数情况下,这就像一个随机顺序。您还想存储每个键第一次出现的顺序。这是解决此问题的一种方法:

my (%count, $line, @display_order);
while ($line = <DATA>) {
    chomp $line;           # strip the \n off the end of $line
    if ($line =~ /\S/) {
        if ($count{$line}++ == 0) {
            # this is the first time we have seen the key "$line"
            push @display_order, $line;
        }
    }
}

# now @display_order holds the keys of %count, in the order of first appearance
foreach my $key (@display_order)
{
    print "$key\t $count{$key}\n";
}
于 2009-10-13T07:34:04.083 回答
10

perlfaq4“如何让我的哈希记住我将元素放入其中的顺序?”的回答


如何让我的哈希记住我将元素放入其中的顺序?

使用 CPAN 中的 Tie::IxHash。

use Tie::IxHash;

tie my %myhash, 'Tie::IxHash';

for (my $i=0; $i<20; $i++) {
    $myhash{$i} = 2*$i;
    }

my @keys = keys %myhash;
# @keys = (0,1,2,3,...)
于 2009-10-13T13:26:06.017 回答
6

简单地:

my (%count, @order);
while(<DATA>) {
  chomp;
  push @order, $_ unless $count{$_}++;
}
print "$_ $count{$_}\n" for @order;
__DATA__
a
b
e
a
c
d
a
c
d
b

或作为一个班轮

perl -nlE'$c{$_}++or$o[@o]=$_}{say"$_ $c{$_}"for@o'<<<$'a\nb\ne\na\nc\nd\na\nc\nd\nb'
于 2009-10-13T11:07:13.090 回答
5

另一种选择是 David Golden (@xdg) 的简单纯 perlHash::Ordered模块。您获得了秩序,但速度较慢,因为散列成为幕后的对象,并且您使用访问和修改散列元素的方法。

可能有一些基准可以量化模块比常规哈希慢多少,但这是在小脚本中使用键/值数据结构的一种很酷的方式,并且在那种应用程序中对我来说足够快。该文档还提到了其他几种排序哈希的方法。

于 2015-03-09T21:36:40.763 回答
4

I'm not convinced that this is always a better technique, but I have used it sometimes. Instead of just having the "seen" type of hash, it can store both the count and order noticed.

Basically, instead of $count{$line} having the number of times seen, $count{$line}{count} is the times seen and $count{$line}{order} is the order in which it was seen.

my %count;
while (my $line = <DATA>) {
    chomp $line;
    if ($line =~ /\S/) {
        $count{$line} ||= { order => scalar(keys(%count)) };
        $count{$line}{count}++;
    }
}

for my $line (sort { $count{$a}{order} <=> $count{$b}{order} } keys %count ) {
    print "$line $count{$line}{count}\n";
}
于 2015-03-09T20:25:22.517 回答
1

在 Perl 中分配之前,哈希只是数组,因此如果将其转换为数组,则可以按原始顺序对其进行迭代:

my @array = ( z => 6,
              a => 8,
              b => 4 );

for (my $i=0; $ar[$i]; ++$i) {
    next if $i % 2;
    my $key = $ar[$i];
    my $val = $ar[$i+1];

    say "$key: $val"; # in original order
}

如果您明显这样做,您将失去哈希索引的好处。但是由于散列只是一个数组,您可以通过将数组分配给散列来创建一个:

my %hash = @array;
say $hash{z};

这可能只是“使用数组作为索引”解决方案的一种变体,但我认为它更简洁,因为不是手动(或以其他方式)输入索引,而是直接从源数组创建它。

于 2019-03-29T13:01:25.513 回答