【问题标题】:How to use regular expression to find keys in hash如何使用正则表达式在哈希中查找键
【发布时间】:2013-12-22 21:06:11
【问题描述】:

我有 6mio 哈希,需要计算其中有多少具有以 AA00AB10 开头的键,以及其中有多少具有以 both 字符串开头的键。

对于每个哈希我都这样做了:

if (exists $hash{AA00}) {
    $AA00 +=1;
}
if (exists $hash{AB10}) {
    $AB10 += 1;
}
if (exists $hash{AA00} and exists $hash{AA10}) {
    $both += 1;
}

但是我只计算包含 AA00AB10 作为键的哈希的数量,但我也想计算包含 AA001 的哈希。我可以为此使用正则表达式吗?

【问题讨论】:

  • 什么是"6mio"?如果您的意思是六百万,那么通常写为 6M
  • 您要求开头的键和包含的键,而您的代码会找到等于的键。我猜你的意思是第一个,对吗?
  • 很抱歉,我们没有想出任何您可以使用的东西,但如果您参与对话以解决您的问题,这将有所帮助。我在这里的 cmets 中问了两个问题,您都没有回答。有一次回复得到了四张赞成票,根本没有帮助你。请帮助我们为您提供帮助。
  • 对不起 6M。是的!我的意思是我的哈希中有多少包含至少一个以 AA00 开头的键,有多少哈希包含至少一个以 AB10 开头的键,最后有多少哈希包含同时以 AA00 和 AB10 开头的键。
  • 好的,谢谢。到目前为止,您给出的答案都没有解决您的问题。您必须将哈希数组 (?) 视为一个简单的键列表数组,并遍历它们中的每一个。如果您不通过特定键访问哈希,则哈希的速度优势将无效,并且如果您经常这样做,那么您可能需要更好的数据结构以将运行时间保持在合理的范围内。

标签: regex perl hash


【解决方案1】:

我完全误解了你的问题。要查找键与正则表达式匹配的 散列 的数量(而不是 single 散列中匹配正则表达式的 的数量),您仍然可以使用我在之前的回答中概述的grep 方法。但是,这一次,您需要遍历您的哈希(如果您有 600 万个哈希,我假设您将它们存储在一个数组中)并在每个哈希上运行两次 grep

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

my @array = (
    { AA00  => 'foo' },
    { AB10  => 'bar' },
    { AA001 => 'foo' },
    { AA00  => 'foo', AB10 => 'bar' }
);

my ($hashes_with_aa00, $hashes_with_ab10, $hashes_with_both) = (0, 0, 0);

foreach my $hash (@array) {
    my $aa_count = grep { /^AA00/ } keys %$hash;
    my $ab_count = grep { /^AB10/ } keys %$hash;

    $hashes_with_aa00++ if $aa_count;
    $hashes_with_ab10++ if $ab_count;
    $hashes_with_both++ if $aa_count and $ab_count;
}

say "AA00: $hashes_with_aa00";
say "AB10: $hashes_with_ab10";
say "Both: $hashes_with_both";

输出:

AA00: 3
AB10: 2
Both: 1

这可行,但在性能方面很差:grep 循环遍历每个哈希键列表中的 每个 元素,我们称它为 两次 em> 每个哈希!

由于我们不关心每个哈希中有多少键匹配,只关心是否存在 匹配,更好的解决方案是 any from List::MoreUtilsany 的工作方式与grep 非常相似,但一旦找到匹配项就会返回。要使用any 而不是grep,请更改:

foreach my $hash (@array) {
    my $aa_count = grep { /^AA00/ } keys %$hash;
    my $ab_count = grep { /^AB10/ } keys %$hash;

    $hashes_with_aa00++ if $aa_count;
    $hashes_with_ab10++ if $ab_count;
    $hashes_with_both++ if $aa_count and $ab_count;
}

到这里:

use List::MoreUtils 'any';

foreach my $hash (@array) {
    my $aa_exists = any { /^AA00/ } keys %$hash;
    my $ab_exists = any { /^AB10/ } keys %$hash;

    $hashes_with_aa00++ if $aa_exists;
    $hashes_with_ab10++ if $ab_exists;
    $hashes_with_both++ if $aa_exists and $ab_exists;
}

请注意,我更改了变量名称以更好地反映它们的含义。

这在性能方面要好得多,但正如 Borodin 在对您的问题的评论中指出的那样,由于不使用特定键访问哈希,您正在失去哈希的速度优势。您可能希望相应地更改数据结构。


原答案:计算与 单个散列中的正则表达式匹配的键

这是我基于对您问题的误解的原始答案。我将其搁置是因为我认为它可能对类似情况有用。

要计算单个哈希中匹配正则表达式的键数,您可以使用grep

my $aa_count = grep { /^AA00/ } keys %hash;
my $ab_count = grep { /^AB10/ } keys %hash;
my $both     = $aa_count + $ab_count;

正如 HunterMcMillen 在 cmets 中指出的那样,无需再次搜索哈希键即可获得总数;在这种情况下,您可以简单地将两个小计相加。您可以摆脱这种情况,因为您要搜索的两种模式是互斥的;换句话说,您不能拥有一个既以AA00 AB10 开头的密钥。

在更一般的情况下,单个键可能会匹配两种模式(感谢 Borodin)。在这种情况下,您不能简单地将两个小计相加。例如,如果您希望您的密钥仅在字符串中的任何位置包含AA00AB10,不一定在开头,您需要执行以下操作:

my $aa_count = grep { /AA00/ } keys %hash;
my $ab_count = grep { /AB10/ } keys %hash;
my $both     = grep { /(?:AA00|AB10)/ } keys %hash;

请注意,这会多次调用grep,这意味着多次遍历整个哈希。这可以使用像 FlyingFrog 和 Kenosis 那样的单个 for 循环更有效地完成。

【讨论】:

  • 不能$both 只是$aa_count + $ab_count
  • @HunterMcMillen 这更有意义。
  • 在单个 for 循环中完成不是更有效,而不是遍历哈希两次?
  • @hwnd 从 5.18 开始,smart match is experimental: “很明显,智能匹配几乎肯定会在未来发生变化或消失。不建议依赖其当前行为。 "
  • @Kenosis:除非存在已知的性能问题,否则最好使用更清晰的代码,而不是节省几纳秒的代码。
猜你喜欢
  • 1970-01-01
  • 2023-04-02
  • 2019-11-10
  • 2021-09-08
  • 2018-01-12
  • 2011-07-15
  • 2021-12-31
  • 2011-07-08
  • 1970-01-01
相关资源
最近更新 更多