我有一个看起来像这样的排序函数:
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
。
答案 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];
}