从模块MoreUtils.pm解释Perl的语法

时间:2017-08-31 13:03:07

标签: perl

我正在寻求从模块MoreUtils.pm解释Perl的uniq和fidrstidx函数的语法。

在寻找之后,我已经知道了从具有重复元素的数组中获取uniq数组元素并通过以下方式从数组中查找第一个索引的其他方法:

## remove duplicate elements ##

my @arr = qw (2 4 2 8 3 4 6);
my @uniq = ();
my %hash = ();
@uniq = grep {!$hash{$_}++ } @arr;

### first index ###

@arr = qw (Java ooperl Ruby cgiperl Python);
my ($index) = grep {$arr[$_] =~ /perl/} 0..$#arr;

任何人都可以向我解释下面的第二行sub uniq函数,包括来自MoreUtils.pm的地图和三元运算符:

 map {$h{$_}++ == 0 ? $_ : () } @;

以及

& @传递给firstidx函数和函数体中的下面一行:

 local *_ = \$_[$i];

据我所知,sub例程ref被传递给firstidx。但是会更加详细地解释一下。

感谢。

2 个答案:

答案 0 :(得分:8)

你的第二个问题已在评论中得到解答。

您的第一个问题是List::MoreUtils询问map {$h{$_}++ == 0 ? $_ : () } @;。在最近的版本中,它实际上在List::MoreUtils::PP(对于Pure Perl),因为许多子例程也在C和XS中实现。这是Pure Perl uniq的当前版本:

sub uniq (@)
{
    my %seen = ();
    my $k;
    my $seen_undef;
    grep { defined $_ ? not $seen{ $k = $_ }++ : not $seen_undef++ } @_;
}

虽然它使用的是map,但它具有相同的grep技术。 grep遍历@_中的所有元素,并且必须为每个元素返回true或false。评估为true的元素最终会出现在输出列表中。然后,代码想要在第一次看到它时将元素求值为true,并在其余时间使用false。

在此代码中,它分别处理undef。如果当前元素不是undef,则它执行条件运算符的第一个分支,否则执行第二个分支。现在让我们来看看分支。

定义的大小写为哈希添加了一个元素。没有人留下关于$k使用的代码评论,但它可能与不扰乱$_有关。 $k成为哈希的关键:

 not $seen{ $k = $_ }++

如果这是第一次遇到该密钥,则哈希值为undef。在使用值之后,该后增量会完成其工作,因此请暂时考虑一下。低优先级not会看到$seen{$k}的值,即undefnotundef的错误值转换为true。这是真的表明grep第一次看到了$_。它成为输出列表的一部分。然后,++执行其工作,并将undef值增加到1。在具有相同值的所有后续遭遇中,哈希值将为真。 not会将true值变为false,并且该元素不会出现在输出列表中。

您展示的map会实现grep。它在条件为真时返回一个元素,在它为假时不返回任何元素:

map {$h{$_}++ == 0 ? $_ : () } @_;

对于每个元素,它将其添加为哈希中的键,并将该值与0进行比较。第一次看到一个元素值为undef。在数字上下文中,undef0。因此,==返回true并且条件运算符的第一个分支触发,将$_返回到输出列表。 ++然后将哈希值从undef增加到1。下次遇到相同的值时,哈希值不是0,条件运算符的第二个分支返回空列表。这不会在输出列表中添加任何元素。

答案 1 :(得分:4)

较新版本的List::MoreUtils don't use the construct any more,但正如Сухой27所解释的那样,

map { CONDITION ? $_ : () } LIST

只是

的替代品
grep { CONDITION } LIST

我认为作者为此实施选择map并没有任何首要原因,实际上在grep的更高版本中将其简化为List::MoreUtils

firstidx语法为firstidx BLOCK LIST。与内置mapgrep一样,指定BLOCK中的代码将对变量$_进行操作,并且允许代码进行更改到 $_。因此,在firstidx实施中,仅为$_中的每个值设置LIST是不够的。相反,$_必须对LIST的每个元素设置别名,以便$_BLOCK的更改也会导致LIST中元素的更改。这是通过操纵符号表

来完成的
local *_ = \$scalar         # make $_ an alias of $scalar

并且您使用local,以便在firstidx完成后,我们没有破坏先前在$_变量中的任何有用信息。