在自定义排序中隐藏“参数不是数字”

时间:2015-08-17 11:37:24

标签: perl sorting

我有一个看起来像这样的排序函数:

sub custom_sort {
    my @a_cols = split( /\|/, $a );
    my @b_cols = split( /\|/, $b );

    return $a_cols[0] <=> $b_cols[0]     #numeric compare first column;
        || $a_cols[1] <=> $b_cols[1]     #second column numeric compare
        || $a_cols[1] cmp $b_cols[1];    #alphabetic compare second column
}

这很适合它的目的 - 列1可以是字母或数字,我想分开对这些事情进行排序。

只有一个问题 - 如果此列不仅仅是数字,我会收到警告。虽然我可以将比较和测试/返回分开 - 是否有更简洁的方法?

我在考虑使用r正则表达式:

$a_cols[1] =~ s/^\D+/0/r 

或者也许是三元运营商。

但有没有更优雅的方式来说“如果它是文本,将其视为零”?

128114529000000|10|0
1212835|A|0
128114529000000|1|0
1212835|G|0
128114529000000|3|0
1212835|T|0
128114529000000|2|0
1212835|H|0
128114529000000|9|0

期望的输出是:

1212835|A|0
1212835|G|0
1212835|H|0
1212835|T|0
128114529000000|1|0
128114529000000|2|0
128114529000000|3|0
128114529000000|9|0
128114529000000|10|0

e.g。基于第一个键排序,然后排序第二个 - 但根据上下文按字母顺序或数字顺序进行比较。香草'alpha'排序不起作用,因为那里有10

2 个答案:

答案 0 :(得分:3)

首先,我注意到你没有预处理你的输入,如果你有一个更大的数据集可能会很糟糕。我用伪Schwartzian变换完成了这个。这也让我摆脱了子程序内部的分裂。

然后我只是检查第二列是否包含现有代码的编号,并使用括号来确保优先级问题。

正如ikegami所提到的,最简单的解决方案可能是在子程序中使用no warnings 'numeric'。虽然这会将非数字和0排序为相同的值。

代码

use strict;
use warnings;

print $_->[-1] for sort custom_sort(),
    map [ split(/\|/, $_),  $_ ], <DATA>;

sub custom_sort {
    return $a->[0] <=> $b->[0]     
        || ($a->[1] =~ /\d/ && $b->[1] =~ /\d/ &&
            $a->[1] <=> $b->[1])     #second column numeric compare
          # ^----------------- this
        || $a->[1] cmp $b->[1];    
}


__DATA__
128114529000000|10|0
1212835|A|0
128114529000000|1|0
1212835|G|0
128114529000000|3|0
1212835|T|0
128114529000000|2|0
1212835|H|0
128114529000000|9|0

输出:

1212835|A|0
1212835|G|0
1212835|H|0
1212835|T|0
128114529000000|1|0
128114529000000|2|0
128114529000000|3|0
128114529000000|9|0
128114529000000|10|0

答案 1 :(得分:1)

你可以使用

no warnings 'numeric';

以下处理负数:

sub custom_sort {
    my @a_cols = split( /\|/, $a );
    my @b_cols = split( /\|/, $b );

    my $a_cols_1_is_text = $a_cols[1] =~ /^-?\d+\z/ ? 0 : 1;
    my $b_cols_1_is_text = $b_cols[1] =~ /^-?\d+\z/ ? 0 : 1;

    return $a_cols[0] <=> $b_cols[0]
        || $a_cols_1_is_text <=> $b_cols_1_is_text
        || $a_cols_1_is_text ? $a_cols[1] cmp $b_cols[1] : $a_cols[1] <=> $b_cols[1];
}