如何在 perl 中对包含两个数字的字符串数组进行排序?

How to sort an array of strings containing two numbers in perl?

我有以下字符串数组:

Expt5_Expt12
Expt5_Expt1
Expt12_Expt2
Expt11_Expt8
Expt1_Expt2
Expt10_Expt1
Expt10_Expt4
Expt11_Expt1

我想按第一个数字对这些字符串进行排序,然后按第二个数字对这些字符串进行排序,这样我就有了一个这样的列表:

Expt1_Expt2
Expt5_Expt1
Expt5_Expt12
Expt10_Expt1
Expt10_Expt4
Expt11_Expt1
Expt11_Expt8
Expt12_Expt2

我只找到了仅按第一个数字或第二个数字排序的解决方案。我用正则表达式和排序函数尝试了一些东西,但我没有找到解决方案。

按功能排序非常简单。该函数必须 return -1、0 或 1,具体取决于 $a$b 是在之前还是之后。 (如评论中所述 - 它可以是任何正值或负值 - 关键是元素是在彼此之前还是之后)。

$a$b 是 'special' 变量,专门用于 perl 和排序。因此,它们不需要声明,并且 真的 将它们用于代码中的其他内容是个坏主意。但是,谁使用单字母变量呢?

所以你的价值观:

#!/usr/bin/env perl
use strict;
use warnings;

sub custom_sort {
   my ( $a1, $a2 ) = ( $a =~ m/(\d+)/g );   #extract the numeric elements
   my ( $b1, $b2 ) = ( $b =~ m/(\d+)/g );

   return ( $a1 <=> $b1     #return the result of this comparison
         || $a2 <=> $b2 );  #unless it's zero, then we return the result of this.
}


my @list = <DATA>;    
print sort custom_sort @list; 

__DATA__
Expt5_Expt12
Expt5_Expt1
Expt12_Expt2
Expt11_Expt8
Expt1_Expt2
Expt10_Expt1
Expt10_Expt4
Expt11_Expt1

你可以把它写得更简洁,但本质是这样的:

  • 提取第一个和第二个值。
  • 然后使用 || 运算符 - 这样如果 $a1 <=> $b1 为零,它会计算表达式的第二部分。
  • <=> 是一个 'less than, equal to, greater than' 运算符,它 returns -1、0 或 1 基于数值比较。对于字符串,您可以使用 cmp 来做同样的事情。

(如果你想为每次比较调试这种排序方式 'working',你可以打印这些,如果你正在做一些复杂的事情,这真的很方便)

这与其他人发布的解决方案大致相同,但通过使用 map

使其更加简洁
use strict;
use warnings;

my @data = <DATA>;

print sort {
    my @ab = map [ /\d+/g ], $a, $b;
    $ab[0][0] <=> $ab[1][0] or $ab[0][1] <=> $ab[1][1];
} @data;

__DATA__
Expt5_Expt12
Expt5_Expt1
Expt12_Expt2
Expt11_Expt8
Expt1_Expt2
Expt10_Expt1
Expt10_Expt4
Expt11_Expt1

输出

Expt1_Expt2
Expt5_Expt1
Expt5_Expt12
Expt10_Expt1
Expt10_Expt4
Expt11_Expt1
Expt11_Expt8
Expt12_Expt2

使用Sort::Key::Multi;

use Sort::Key::Multi qw(i2_keysort); #i2 means two integer keys

my @sorted = i2_keysort { /(\d+)\D+(\d+)/ } @data;