我正在寻求从模块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。但是会更加详细地解释一下。
感谢。
答案 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}
的值,即undef
。 not
将undef
的错误值转换为true。这是真的表明grep
第一次看到了$_
。它成为输出列表的一部分。然后,++
执行其工作,并将undef
值增加到1
。在具有相同值的所有后续遭遇中,哈希值将为真。 not
会将true值变为false,并且该元素不会出现在输出列表中。
您展示的map
会实现grep
。它在条件为真时返回一个元素,在它为假时不返回任何元素:
map {$h{$_}++ == 0 ? $_ : () } @_;
对于每个元素,它将其添加为哈希中的键,并将该值与0进行比较。第一次看到一个元素值为undef
。在数字上下文中,undef
为0
。因此,==
返回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
。与内置map
和grep
一样,指定BLOCK
中的代码将对变量$_
进行操作,并且允许代码进行更改到 $_
。因此,在firstidx
实施中,仅为$_
中的每个值设置LIST
是不够的。相反,$_
必须对LIST
的每个元素设置别名,以便$_
内BLOCK
的更改也会导致LIST
中元素的更改。这是通过操纵符号表
local *_ = \$scalar # make $_ an alias of $scalar
并且您使用local
,以便在firstidx
完成后,我们没有破坏先前在$_
变量中的任何有用信息。