在perl中按字母和十六进制进行多次排序

时间:2017-08-30 02:39:01

标签: perl sorting

我想按十六进制数字和字母对数组进行排序,但我不知道怎么做。我的数组看起来像这样:

my @array = ( 
    "{John}{1} is a boy", "{Emily}{a} is a girl", ..., "{Alba}{f} is a brand"
);

我想首先按字母顺序在第一个{}中对名称进行排序,然后按第二个{}按十六进制数排序,而不管后面的单词。

my @sorted = sort { $a <=> $b } @array;
my @sorted1 = sort { hex $a <=> hex $b } @sorted;
print "@sorted1\n";

我尝试这样的东西,但它不起作用。我如何按照上面描述的方式对数组进行排序?

2 个答案:

答案 0 :(得分:4)

sort {
   my @a_keys = $a =~ /\{([^{}]*)\}/g;
   my @b_keys = $b =~ /\{([^{}]*)\}/g;

   $a_keys[0] cmp $b_keys[0]
      ||
   hex($a_keys[1]) <=> hex($b_keys[1])
}

答案 1 :(得分:1)

我希望你的代码不起作用的原因很明显。

my @sorted = sort { $a <=> $b } @array;
my @sorted1 = sort { hex $a <=> hex $b } @sorted;

这里有两个问题。首先,你的第二种方法就是覆盖第一种。你运行的第一种是完全没有意义的。您的代码与以下内容具有完全相同的效果:

my @sorted1 = sort { hex $a <=> hex $b } @array;

其次,你正在按错误的方式排序。你的第一个排序是按整个字符串排序,第二个排序是通过hex()函数传递的整个字符串排序(假设你的字符串不是有效的十六进制字符串,那不是一个好主意)。

让我们分别解决这两个问题。

按两种不同的方式排序实际意味着什么?通常,它意味着各种等级 - 一种比另一种更重要。这意味着“按此排序,如果两个记录具有相同的值,则按此次要值对其进行排序”。在你的情况下,你可能会说“按第一对大括号之间的字符串排序,如果两个记录在那里有相同的值,那么按第二对大括号之间的十六进制值排序”。当第一个排序值在两个记录中相同时,第二个排序值是“打破平局”。

而Perl的sort非常适合这样的多层次排序。当你编写一个排序表达式时,它需要返回-1,0或1.如果你比较的两个值不同,它返回-1或1,如果它们是相同的则返回0。并且,由于0是Perl的假值,因此可以轻松地将排序堆叠在一起。

my @sorted = sort {
   sort_expression_1
     or
   sort_expression_2
} @array;

如果sort_expression_1返回-1或1,那么这是真的,布尔表达式“短路”。如果两个值相同,则sort_expression_1返回0并且布尔表达式继续评估sort_expression_2

那么我们真正想要排序的是什么?好吧,我们需要在大括号之间提取字符串并按那些排序。这样的事情,也许是:

my @sorted = sort {
  # Extract the sortable bits into arrays
  my @a_bits = $a =~ /\{(.+?)}/g;
  my @b_bits = $b =~ /\{(.+?)}/g;

  # Compare the strings
  $a_bits[0] cmp $b_bits[0]
    or
  # If the strings are the same, compare the hex numbers
  hex($a_bits[1]) <=> hex($b_bits[i])
} @array;

为了更有效地排序,您可以使用“Schwartzian Transform”在开始实际排序之前打破您要排序的位。

my @sorted =
  map { $_->[0] }
  sort { $a->[1] cmp $b->[1] or hex($a->[2]) <=> hex($b->[2]) }
  map { [ $_, /\{(.+?)}/g ] } @array;