【问题标题】:Perl: safely make hash from list, checking for duplicatesPerl:安全地从列表中生成哈希,检查重复项
【发布时间】:2015-01-07 19:21:30
【问题描述】:

在 Perl 中,如果您有一个包含偶数个元素的列表,您可以直接将其转换为哈希:

my @a = qw(each peach pear plum);
my %h = @a;

但是,如果有重复的键,那么它们将被静默接受,最后一次出现的是使用的键。我想做一个哈希检查是否有重复:

my @a = qw(a x a y);
my %h = safe_hash_from_list(@a);    # prints error: duplicate key 'a'

显然我可以自己编写该例程:

sub safe_hash_from_list {
    die 'even sized list needed' if @_ % 2;
    my %r;
    while (@_) {
        my $k = shift;
        my $v = shift;
        die "duplicate key '$k'" if exists $r{$k};
        $r{$k} = $v;
    }
    return %r;
}

然而,这比简单的赋值要慢很多。此外,如果有一个 CPAN 模块已经完成相同的工作,我不想使用我自己的私有例程。

在 CPAN 上是否有合适的例程可以安全地将列表转换为哈希?理想情况下,它比上面的纯 Perl 实现快一点(尽管可能永远不会像简单的赋值那样快)。

如果允许我提出相关的后续问题,我还想知道一个绑定的哈希类,它允许每个键只分配一次并在重新分配时死亡。这将是上述问题的更一般情况。同样,我可以自己编写这样的绑定哈希,但我不想重新发明轮子,如果已经存在,我更喜欢优化的实现。

【问题讨论】:

  • 是什么让你觉得这很慢?我认为您很难找到更快的模块。你需要对它进行基准测试,我严重怀疑你会从中获益多少。例如,您可以使用 List::MoreUtils uniq,例如if (@a == uniq(@a))。但是那个子程序很简单,sub uniq { my %seen; grep { !$seen{$_}++ } @_; }
  • 它可能会尽可能快,但肯定比简单的赋值 %h = @a 慢。测试 @a == uniq(@a) 不能满足我的要求,因为只有键(偶数位置元素)需要是唯一的,而不是值。
  • 好吧,那么你会知道keys %h 应该等于@a / 2,不是吗?
  • 是的,我同时也有同样的想法 ;-)
  • 伟大的思想都一样。 ;)

标签: perl list hash duplicates checked


【解决方案1】:

检查没有重复的键的快速方法是计算键并确保它们等于列表中项目数的一半:

my @a = ...;
my %h = @a;
if (keys %h == (@a / 2)) {
    print "Success!";
}

【讨论】:

  • 天真的问题:如果 ARRAY 有像 qw/a b c/ 这样的奇怪元素?
  • 那么你一开始就不能强制它进入哈希。 Odd number of elements in hash assignment
  • @sputnick 然后哈希的最后一个值是 undef 和 warnings 抱怨。 keys %h 是 2,@a / 2 是 1.5
  • LIST 版本:sub { my $h = shift; %$h = @_; warn("...") if keys(%$h) != @_/2; }->(my %h, LIST);
  • ikegami:我认为您需要将哈希引用作为第一个参数传递。但是,是的,这可以成为一个通用的 set_hash(\%h, k=>v...) 例程,避免按值返回散列的开销。
猜你喜欢
  • 2013-11-29
  • 2019-04-13
  • 1970-01-01
  • 1970-01-01
  • 2019-04-28
  • 2011-05-19
  • 2011-12-28
  • 1970-01-01
  • 2018-06-18
相关资源
最近更新 更多