【发布时间】:2012-02-02 23:15:58
【问题描述】:
我正在尝试在 Perl 中实现 Tcl 字典排序,以便对某些文件进行排序。对于不了解 Tcl 的人,你可以得到它来按值对连续整数进行排序,这里有详细说明:
http://www.perlmonks.org/index.pl?node_id=160157
总结一下: 给定数组:
qw(
bigbang
x10y
x9y
bigboy
bigBoy
x11y
)
按不区分大小写的字母排序,然后区分大小写作为 tie-breaker,然后按数字排序,除了它接受任何后续数字并将整个事物解释为排序中的单个数字,因此上面的结果为:
qw(
bigbang
bigBoy
bigboy
x9y
x10y
x11y
)
x9y 出现在 x10y 和 x11y 之上,而在标准 ASCII 排序中,x10y 和 x11y 将出现在 x9y 之上,因为 1 出现在 9 之前。
我试图将该链接中的 Juerd 示例实现为一个函数,但在我的情况下,当我有一个版本号列表时,排序完美地模仿了 Tcl 字典排序,如下所示:
qw{ 1 1.0 1.01 1.2 1.02 1.0003 1.102 1.103 1.203 102a 102b 103a 103b 123 };
但是当文件使用绝对路径时,顺序就会混乱。
我在下面发布了一个示例脚本。如果有人能看出函数出错的原因,或者您可以提出更现代的替代方案(因为我工作的示例是 10 年前发布的:P),我将不胜感激。
如果您想查看 Tcl 字典排序的实际效果,请查看以下链接:
提前致谢!
编辑:-感谢choroba 引导我找到解决方案!工作函数如下:
sub dict_sort {
my @unsorted = @_;
my @sorted =
map $_->[0],
sort {
my $i = 0;
{
my $A = $a->[1][$i];
my $B = $b->[1][$i];
defined($A) || defined($B) # Stop if both undef
and (
defined($A) <=> defined($B) # Defined wins over undef
or (
$A !~ /\d/ || $B !~ /\d/ # $A or $B is non-integer
? (lc $A cmp lc $B) # ?? Stringy lowercase
|| ( $A cmp $B) # -> Tie breaker
: $A <=> $B # :: $A and $B are integers
or (
length($A) <=> length($B) # If numeric comparison returns the same, check length to sort by leading zeroes
)
)
or ++$i && redo # tie => next part
);
}
}
map [ $_, [ split /(\d+)/ ] ], @unsorted;
return @sorted;
}
【问题讨论】:
-
澄清一下,Tcl 的字典排序将每个单词分成交替的只有数字和非数字的字符串。数字序列按数字排序(当两个值相同时处理前导零有一点魔法),非数字序列按不区分大小写的 ASCII 排序。比较以数字开头的键与以非数字开头的键时,前导数字在前。
-
在任何人问之前,这给了用户似乎认为文件名应该排序的一个非常好的近似值。
-
嗯,前导零可能会在这里引起问题,但我很惊讶我没有看到版本字符串和绝对路径出现相同的错误行为。
-
在任何人提到任何事情之前,我个人知道 Tcl 字典排序给出的顺序不一定是“正确”的,但它用于我无法控制的另一段代码,并且不能改变,但必须模仿才能产生相同的结果:)