2

我需要一个可以与排序一起使用的 Perl 比较函数。

每个键都是一个文本字符串,具有任意数量的子键,由分隔符(点、冒号、空格和斜杠)分隔。一些子键是数字的,需要按数字排序。密钥格式和子密钥数量各不相同。因此,比较必须处理一个键长于另一个键,并且必须处理子键在一个键中是数字但在另一个键中不是数字的情况(在这种情况下,文本比较适用于该子键)。

这行得通,但我敢打赌有更好的解决方案:

use warnings;
use strict;
use Scalar::Util qw[looks_like_number];

sub hier_cmp {

    my $aa = $a;
    my $bb = $b;

    # convert all delims (. : / space) to the same delim

    $aa =~ tr/.:\/ /::::/;
    $bb =~ tr/.:\/ /::::/;
    my @lista = split(":", $aa);
    my @listb = split(":", $bb);

    my $result;

    for my $ix (0 .. min($#lista, $#listb)) {
        if (exists($lista[$ix]) && exists($listb[$ix])) {
            if ( looks_like_number($lista[$ix]) && looks_like_number($listb[$ix])) {
                # compare numerically
                $result = ($lista[$ix] <=> $listb[$ix]);
            } else {
                # compare as strings
                $result = ($lista[$ix] cmp $listb[$ix]);
            }
            if ($result == 0) {
                next;
            }
            return $result;

        } elsif (exists($lista[$ix])) {
            return 1;
        } else {
            return -1;
        }
    }
}

就我而言,可读性比速度更重要。这只是一个内部工具,列表很少有超过数百个元素。但是,任何学习东西的机会都是好的。

如您所见,我不是 perl 向导。即使对我的代码进行微不足道的改进也将不胜感激。

谢谢!

4

2 回答 2

2

这看起来像自然排序。CPAN 上有几个模块已经这样做了,例如Sort::NaturallySort::Key::Natural

例如:

use Sort::Key::Natural qw(natsort);
my @sorted = natsort @data;
于 2012-07-17T18:52:13.820 回答
1

如果你给我们一些数据来测试会有所帮助,但是这段代码通过了一些基本的测试并且看起来是正确的。

它通过使用该List::MoreUtils函数pairwise创建字段对数组来简化问题。

然后只需检查是否只定义了一个,当其中一个列表先于另一个结束并且应该首先排序时;如果它们都是数字,则应将它们与数字比较进行比较;或者简单地将它们作为字符串进行比较。

如果到达对数组的末尾,则所有内容都匹配并返回零以表示相等。

更新

我已更改此代码以删除对List::MoreUtils::pairwise.

use strict;
use warnings;

use Scalar::Util 'looks_like_number';

sub hier_cmp {

  our ($a, $b);

  my @a = split m|[.: /]+|, $a;
  my @b = split m|[.: /]+|, $b;

  for my $i (0 .. $#a > $#b ? $#a : $#b) {
    my @ab = ( $a[$i], $b[$i] );
    if (grep defined, @ab < 2) {
      return defined $ab[0] ? 1 : -1;
    }
    else {
      my $numeric = grep(looks_like_number($_), @ab) == 2;
      my $result = $numeric ? $ab[0] <=> $ab[1] : $ab[0] cmp $ab[1];
      return $result if $result;
    }
  }

  return 0;
}
于 2012-07-17T19:16:01.103 回答