Perl中向Hash推送数据出现异常行为的原因及解决方法
为什么哈希遍历结果随机,数组却固定?
这问题我太熟了!你碰到的其实就是Perl哈希最经典的特性——无序性搞的鬼:虽然哈希的键值对是严格一一对应的,但哈希本身完全不会记住你插入键的顺序,每次用keys %store获取键列表时,顺序都是由Perl内部哈希算法决定的,看起来就像随机的。
两种写法的核心区别
- 数组版本:你把拼接后的字符串按输入顺序
push到数组@arr里,遍历数组的时候是严格按照元素插入顺序来的,所以每次输出都是固定的,完全符合你的预期。 - 哈希版本:你把每个拼接后的字符串作为哈希的键,当用
keys %store取键的时候,这些键的顺序和你插入的顺序没有任何关系,完全是随机打乱的。这就导致你拆分后打印的内容顺序混乱,甚至出现像你例子里spoons跑到Crockery相关内容后面的情况——只是因为它对应的哈希键被随机排到了那个位置而已。
解决方案:用有序哈希
既然你不想额外用数组,那可以直接用Perl的有序哈希实现,让哈希记住插入顺序。这里有两种常用方式:
方式1:使用Tie::IxHash(兼容旧版本Perl)
这是一个老牌的Perl模块,从Perl 5.10开始就支持,用法很简单:
use Tie::IxHash; my @Input = ("Crockery breakable plates", "Crockery breakable glasses", "Cutlery bendable forks", "Cutlery bendable spoons" ); my %store; tie %store, 'Tie::IxHash'; # 把普通哈希绑定成有序哈希 foreach (@Input) { my ($key, $key2, $value) = split; chomp ($key, $key2, $value); my $combine = "$key-$key2-$value"; push @{$store{$combine}}, "$key-$key2-$value"; } foreach my $k(keys %store) { ($key, $key2, $value) = split /-/, $k; chomp ($key, $key2, $value); $seen{$key}++ || print "1. $key\n"; $seen{$key2}++ || print "2. -- $key2\n"; $seen{$value}++ || print "3. ----$value\n"; }
方式2:使用Hash::Ordered(Perl 5.20+推荐)
如果你用的是Perl 5.20及以上版本,推荐用核心模块Hash::Ordered,它的性能比Tie::IxHash更好:
use Hash::Ordered; my @Input = ("Crockery breakable plates", "Crockery breakable glasses", "Cutlery bendable forks", "Cutlery bendable spoons" ); my $store = Hash::Ordered->new; # 创建有序哈希对象 foreach (@Input) { my ($key, $key2, $value) = split; chomp ($key, $key2, $value); my $combine = "$key-$key2-$value"; # 初始化或追加元素到哈希值的数组中 my $existing = $store->get($combine) // []; push @$existing, $combine; $store->set($combine, $existing); } foreach my $k($store->keys) { # 用对象的keys方法获取有序键列表 ($key, $key2, $value) = split /-/, $k; chomp ($key, $key2, $value); $seen{$key}++ || print "1. $key\n"; $seen{$key2}++ || print "2. -- $key2\n"; $seen{$value}++ || print "3. ----$value\n"; }
不管用哪种方式,最终遍历哈希键的顺序都会和你插入的顺序一致,输出也就和数组版本完全相同了。
内容的提问来源于stack exchange,提问作者user8956060
相关产品推荐
相关产品推荐

