使用Perl对具有特定字母顺序的字符串进行排序

时间:2015-04-13 18:00:25

标签: perl sorting cpan

我正在尝试使用Perl对具有特定字母顺序的名称列表进行排序,以执行某些特殊功能 排序的工作方式与sort { $a cmp $b }的工作方式相同,但使用不同的字母序列 例如,使用任意字符顺序排序“abdrtwsuiopqe987654”......

我尝试处理sort { $a myFunction $b },但我是Perl的新手,我不知道如何正确组织myFunction以获得我想要的东西。

  • 是否有提供此功能的特定功能(包)?
  • 您是否有自定义排序函数处理字符串的示例?
  • 你知道Perl实现的cmp函数是如何(或在哪个源文件中)看它是如何工作的?

2 个答案:

答案 0 :(得分:9)

以下可能是最快的 [1]

sub my_compare($$) {
    $_[0] =~ tr{abdrtwsuiopqe987654}{abcdefghijklmnopqrs}r
       cmp
    $_[1] =~ tr{abdrtwsuiopqe987654}{abcdefghijklmnopqrs}r
}

my @sorted = sort my_compare @unsorted;

或者如果你想要更具动感的东西,以下可能是最快的 [2]

my @syms = split //, 'abdrtwsuiopqe987654';
my @map; $map[ord($syms[$_])] = $_ for 0..$#syms;

sub my_compare($$) {
    (pack 'C*', map $map[ord($_)], unpack 'C*', $_[0])
       cmp
    (pack 'C*', map $map[ord($_)], unpack 'C*', $_[1])
}

my @sorted = sort my_compare @unsorted;

我们可以逐个字符地进行比较,但是更慢。

use List::Util qw( min );

my @syms = split //, 'abdrtwsuiopqe987654';
my @map; $map[ord($syms[$_])] = $_ for 0..$#syms;

sub my_compare($$) {
    my $l0 = length($_[0]);
    my $l1 = length($_[1]);
    for (0..min($l0, $l1)) {
       my $ch0 = $map[ord(substr($_[0], $_, 1))];
       my $ch1 = $map[ord(substr($_[1], $_, 1))];
       return -1 if $ch0 < $ch1;
       return +1 if $ch0 > $ch1;
    }

    return -1 if $l0 < $l1;
    return +1 if $l0 > $l1;
    return 0;
}

my @sorted = sort my_compare @unsorted;

  1. 从技术上讲,使用GRT可以加快速度。

    my @sorted =
       map /\0(.*)/s,
       sort
       map { tr{abdrtwsuiopqe987654}{abcdefghijklmnopqrs}r . "\0" . $_ }
       @unsorted;
    
  2. 从技术上讲,使用GRT可以加快速度。

    my @sorted =
       map /\0(.*)/s,
       sort
       map { ( pack 'C*', map $map[ord($_)], unpack 'C*', $_ ) . "\0" . $_ }
       @unsorted;
    

  3. cmpscmp运营商实施。

    $ perl -MO=Concise,-exec -e'$x cmp $y'
    1  <0> enter
    2  <;> nextstate(main 1 -e:1) v:{
    3  <#> gvsv[*x] s
    4  <#> gvsv[*y] s
    5  <2> scmp[t3] vK/2
    6  <@> leave[1 ref] vKP/REFC
    

    scmp运算符由pp.c中的pp_scmp函数实现,当sv_cmp_flags use locale; sv_cmp_flags时{{1}}实际上只是{{1}}的包装器没有效果。 {{1}}使用C库函数sv.c或UTF-8识别版本(取决于标量的类型)。

答案 1 :(得分:0)

use Sort::Key qw(keysort);
my @sorted = keysort { tr/abdrtwsuiopqe987654/abcdefghijklmnopqrs/r } @data;

或者在不支持r

中的tr/.../.../r标记的较旧的perls中
my @sorted = keysort { my $key = $_;
                       $key =~ tr/abdrtwsuiopqe987654/abcdefghijklmnopqrs/;
                       $key } @data;

您还可以为此类数据创建专门的排序子例程,如下所示:

use Sort::Key::Maker 'my_special_sort',
                     sub { tr/abdrtwsuiopqe987654/abcdefghijklmnopqrs/r },
                     qw(string);

my @sorted = my_special_sort @data;
my @sorted2 = my_special_sort @data2;