Perl:有人可以解释这段代码吗?它涉及map,sort,tr和references。 (改进的Schwartzian变换)

时间:2011-05-29 04:02:57

标签: perl

我已经在地图,tr和引用上阅读了教程和perldoc,但这个代码对于像我这样的初学者Perl用户来说有点过于先进。

print map $_->[1], 
sort {
$a->[0] cmp $b->[0] ##first element of the array
or $a->[1] cmp $b->[1] } 
map [ tr/"MATCH"/"MATCH"/, $_ ], @allmatches; 

所以我特别需要的是:$ _指的是什么(未定义?)

包括地图在内的最后一行做了什么?

我还不太了解$ a和$ b概念。他们指的是什么? @allmatches的第一个和下一个元素?

此外,所有逗号(在地图之后)做什么?如果这就像Schwartzian变换一样好,因为我还不明白,尽管阅读。

这是我的想法:
将未定义的标量映射为对数组的引用(?)同时调用第二个元素:[1]。它首先根据“MATCH”的出现次数然后按字母表排序我的@allmatches数组。通过参考的第二张地图对我来说很粗糙(地图在一步中做了很多); tr返回次数。第二个“MATCH”没用,但为什么呢?

奖励:我可以用什么替换tr ///来进行更多排序,如果可能的话:tr / MATCH#\ d + // ??

3 个答案:

答案 0 :(得分:14)

从右向左阅读(即按执行顺序)......

map [ tr/"MATCH"/"MATCH"/, $_ ], @allmatches;

对于@allmatches的每个元素e,这将创建对双元素数组的引用,其第一个元素是数字,第二个元素是e。地图的结果是这些参考的数组。

tr/"MATCH"/"MATCH"/计算字母M,A,T,C或H出现在e中的次数。 (技术上用M代替M,用A代替A,用T代替T等,并计算它做了多少这样的替换。)

实际上,它也在计算引号字符,因为tr ///将处理与其他任何内容相同的字符。这似乎是一个错误。

无论如何,让我们说这些引用中的每一个引用一个数组[n,e],其中n是奇怪的数,e是@allmatches的原始元素。

然后“sort”对引用数组进行排序,主要由n(解释为字符串,而不是数字;这似乎是另一个错误),其次是字符串e。

最后,最外面的“map”在排序完成后从每个双元素数组中提取第二个元素(e)。所以最终的结果就是对@allmatches的元素做一个奇怪的(我相信,错误的)排序。

[编辑:正如cjm在评论中指出的那样,此map sort map成语称为Schwartzian transform。]

答案 1 :(得分:10)

不要从右到左阅读;格式化得更好(原版是残酷的),然后从下到上阅读:

print map  { $_->[1] }
      sort {
              $b->[0] <=> $a->[0]
                      ||
              $a->[1] cmp $b->[1]
           }
      map  { [ tr/MATCH// => $_ ] }
      @allmatches;

或者使用更灵活的哈希值:

print map  { $_->{DATA} }
      sort {
              $b->{COUNT} <=> $a->{COUNT}
                          ||
              $a->{DATA}  cmp $b->{DATA}
           }
      map  {
             +{
                COUNT  => tr/MATCH//,
                DATA   => $_,
              }
      } @allmatches;

这当然与此相同:

print map  {         $$_{DATA}      }
      sort {
              $$b{COUNT} <=> $$a{COUNT}
                          ||
              $$a{DATA}  cmp $$b{DATA}
           }
      map  {
             +{
                  COUNT  => tr/MATCH//,
                  DATA   => $_,
              }
      } @allmatches;

看看它有多好?另外,当你从头到尾阅读它时,它对应于一个完全直截了当的shell风格的数据流:

  map @allmatches | sort | map | print

更容易理解
  print(map(sort(map @allmatches)))

并且是每个人都喜欢shell的数据流模型的原因。

答案 2 :(得分:3)

哎哟,同样哎呀......

print map $_->[1], 
            sort {
          $a->[0] cmp $b->[0] ##first element of the array
          or $a->[1] cmp $b->[1] } 
      map [ tr/"MATCH"/"MATCH"/, $_ ], @allmatches;

sort部分相对简单。

sort { $a->[0] cmp $b->[0] or $a->[1] cmp $b->[1] } ...an array...

数组的每个元素本身都是一个数组引用,并且比较执行数组引用的第一个元素的字符串比较(cmp),如果它们相等(cmp返回0 ),第二个元素。

因此,输出是一个排序数组。这留下了两块代码来剖析。第一行和最后一行。最后一行运行map

map [ tr/"MATCH"/"MATCH"/, $_ ], @allmatches

由于tr///运算符中的左右字符串相同,因此显然执行无操作转换;这有点令人费解。 [更新tr///计算每个字母MATCH出现在字符串中的次数;在map的'block'或'expr'中,$_是一个特殊变量 - 被映射的值。]但它需要@allmatches的每个元素并映射它,并输出将其传递给排序。方括号形成一个数组ref,因此输出是一个数组引用数组;每个数组引用都包含单词中MATCH的字母数,后跟单词。

第一行是:

print map $_->[1], ...output from sort...;

这将从已排序的输出中提取名称$_->[1]

  • 总的来说,效果是按顺序列出@allmatches中的单词,使得来自MATCH的字母数量最少(可能为零)的字母按字母顺序排在第一位,然后是下一个最少的字母。来自MATCH的字母(再次按字母顺序排列),等等。

压缩中的巡回演出如果有人提供给我审查,他们会回到绘图板。更新:因为这是一个已知的习语(Schwartzian Transform ),发送回来的唯一原因是“没有足够仔细地布置”和“没有注释为施瓦兹变换”。)

# Schwartzian Transform: sort by number of letters from MATCH and alphabetically
print map  { $_->[1] } 
      sort { $a->[0] <=> $b->[0] or $a->[1] cmp $b->[1] } 
      map  { [ tr/"MATCH"/"MATCH"/, $_ ] }
      @allmatches;

(这正确地使用了第一项的数字比较。)

您提到对$a$b感到困惑。它们基本上是魔术变量 - 排序中比较函数的参数。如果$a小于$b,则比较必须返回负值;如果$a大于$b,则比较必须为正,如果它们相等,则必须返回零。它们($a$b)是需要两个名称时使用的名称; $_map(以及grep和其他列表转换函数)一起使用,其中只需要一个名称。