使用Perl按特定字母顺序排序字符串

7
我正在尝试使用Perl对名称列表进行排序,并采用特定字母顺序以执行一些特殊功能。
排序方式与 sort { $a cmp $b } 相同,但字母的顺序不同。例如,使用任意字符顺序 "abdrtwsuiopqe987654" ... 进行排序。
我尝试使用 sort { $a myFunction $b },但我是Perl的新手,我不知道如何正确组织 myFunction 来实现我的目标。
  • 是否有提供此功能的特定函数(软件包)?
  • 您是否有处理字符串的自定义排序函数示例?
  • 您是否知道如何(或在哪个源文件中)实现了Perl的cmp函数以查看其工作原理?
2个回答

11

以下可能是最快的[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;
从技术上讲,使用GRT可以使其更快。
 my @sorted =
    map /\0(.*)/s,
    sort
    map { tr{abdrtwsuiopqe987654}{abcdefghijklmnopqrs}r . "\0" . $_ }
    @unsorted;
  • 从技术上讲,可以使用GRT使其更快。

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

    cmp 是由 scmp 运算符实现的。

    $ 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 函数实现,它实际上只是在未启用 use locale; 时对 sv.c 中的 sv_cmp_flags 进行了包装。当标量的类型不同时,sv_cmp_flags 会使用 C 库函数 memcmp 或者一个支持 UTF-8 的版本。


    最好缓存这些音译,或者至少记录重复计算的情况,以防出现重复。 - Hunter McMillen
    @Hunter McMillen,我认为收益不会太大,但我已经添加了GRT解决方案。 - ikegami
    逐个比较字符确实是我想要的。回答很好! - JeanJouX
    为什么?这可能慢了100倍! - ikegami

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

    或者在不支持 tr/.../.../r 中的 r 标记的旧版 Perl 中

    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;
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接