理解代码:Hash,grep for duplicatelicates(修改为检查多个元素)

时间:2011-07-09 18:23:49

标签: perl

代码:

@all_matches = grep
{
    ! ( $seensentence
    {
        $_->[0] .'-'. $_->[1] .'-'. $_->[5]
    }
    ++ )
}
@all_matches;

目的:此代码会删除数组@all_matches中某些元素的重复项。

我完全崩溃的尝试(在......我不确定的地方......)

Grep返回返回true的@all_matches元素。

哈希%seensentence的关键是三个元素? @all_matches。由于哈希只能有唯一键,因此第一次通过它的值从undef(0)递增到1.下一次,它是一个定义的值,但!表示grep只返回它undef(与该元素相关的唯一值)。


我的问题:

(1)如何将{$_->[0] .'-'. $_->[1] .'-'. $_->[5]}++变成HoH?

我被告知这是实现它的另一种(惯用的)方式。在黑暗中刺伤将是:

( {$_->[0] => 0,
$_->[1] => 0,
$_->[5] => 0} )++

(1b)因为我不明白原作是如何做我想要的。我读到-bareword等同于"-bareword",所以我尝试了{"$_->[0]" . "$_->[1]". "$_->[5]"},它似乎完全相同。我仍然不明白:它是将每个元素分别视为一个键(a)(如一个键数组)还是(b)正确:所有同时(因为.将它们连接成一个字符串)或是(c)不按我认为的那样做?

(2)这是什么意思:$_->[0] || $_->[1] || $_->[5]?它与上面的内容不同。

我读到:短路逻辑运算符返回最后一个值,所以它会检查{$_->[0]}处的值,如果有一个值,我认为那里的值会增加,如果没有,它会检查下一个元素,直到没有为真,这是当grep传递唯一值时。


感谢您的时间,我尽量做到尽可能彻底(对于错误?)但是如果有任何遗漏,请告诉我。

2 个答案:

答案 0 :(得分:5)

首先让我们将grep转换为foreach循环,以便我们可以更清楚地检查它。为了清楚起见,我将把一些习语扩展成更大的构造。

my @all_matches = ( ... );
{
    my %seen;
    my @no_dupes;
    foreach my $match ( @all_matches ) {
        my $first_item  = $match->[0];
        my $second_item = $match->[1];
        my $third_item  = $match->[5];
        my $key = join '-', $first_item, $second_item, $third_item;
        if( not $seen{ $key }++ ) {
            push @no_dupes, $match;
        }
    }
    @all_matches = @no_dupes;
}

换句话说,原始编码器使用$ match中保存的数组引用为$match->[0]15的每个引用索引创建哈希键。由于散列键是唯一的,因此在推入@no_dupes之前检查密钥是否已存在,将删除任何重复项。

grep{}机制只是一个更有效的代码(即,更快的类型,没有一次性变量)成语来完成同样的事情。如果它有效,为什么要重构呢?你需要改进的是什么不做?

要对HoH做同样的事情,你可以这样做:

my @all_matches = ( ... );
{
    my %seen;
    my @no_dupes;
    foreach my $match ( @all_matches ) {
        my $first_item  = $match->[0];
        my $second_item = $match->[1];
        my $third_item  = $match->[5];
        if( not $seen{ $first_item }->{ $second_item }->{ $third_item }++ ) {
            push @no_dupes, $match;
        }
    }
    @all_matches = @no_dupes;
}

可以将其翻译成grep,如下所示:

my @all_matches = ( ... );
{
    my %seen;
    @all_matches = grep { not $seen{$_->[0]}->{$_->[1]}{$_->[5]}++ } @all_matches;
}

但是,在这种情况下,我没有看到构建数据结构的明显优势,除非您打算稍后使用%seen来做其他事情。

对于||运算符,这是一种不同的动物。我想不出在这种情况下使用它的任何有用方法。例如,“$a || $b || $c”的逻辑短路运算符测试$a的布尔真值。如果是,则返回其值。如果它是假的,它会以同样的方式检查$b。如果它是假的,它会以同样的方式检查$c。但如果$a为真,则永远不会检查$b。如果$b为真,则永远不会检查$c

答案 1 :(得分:4)

$ seensentence的关键是一个简单的字符串。表达式$_->[0] .'-'. $_->[1] .'-'. $_->[5]构造一个字符串。这是一个等价的表达式:join '-', $_->[0], $_->[1], $_->[5]。它似乎假设元素0,1和5足以识别@all_matches中的重复项。

修改
错过了你的上一个问题。

$_->[0] || $_->[1] || $_->[5]返回

  • $_->[0]如果$_->[0]不为假(0,空字符串,未定义),
  • $_->[1]如果$_->[1]不是假,
  • $_->[5]否则。

一旦有意义停止,快捷操作员就会停止。在||的情况下,只要结果是某个非假值,就会出现这种情况。在&&的情况下,只要结果为假,就会这样。