【问题标题】:Odd Perl 'dictionary' sort behaviour奇怪的 Perl '字典' 排序行为
【发布时间】: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),我将不胜感激。

http://pastebin.com/WM6QhzSK

如果您想查看 Tcl 字典排序的实际效果,请查看以下链接:

http://pastebin.com/h3qMT4C2

提前致谢!

编辑:-感谢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 字典排序给出的顺序不一定是“正确”的,但它用于我无法控制的另一段代码,并且不能改变,但必须模仿才能产生相同的结果:)

标签: perl tcl sorting


【解决方案1】:

您的代码对版本字符串的工作方式没有什么不同。只需按此顺序9.02 9.2 添加到列表中。如果你想让02在2之后,你必须检查$A == $B时的情况。

更新:意思是在$A &lt;=&gt; $B后面加上or length $A &lt;=&gt; length $B

【讨论】:

  • 所以它并没有改变顺序,因为当它比较 1.02 和 1.2 时,它会检查 02 和 2,并在执行 $A $B 排序尝试时将它们视为相同?嗯,当 $A $B 返回相同时,让它再次检查前导 0 的数量会更理想,然后再继续进行其余的排序
  • 谢谢!当数字匹配时,我扩展了搜索以检查比较字符串的长度 - 因为如果一个比另一个长,但在数字比较中是相同的值,它必须有前导零 - 它似乎很有效: ) 我将编辑我的代码并输入工作函数,并将你的代码标记为正确的点。您能否编辑您的答案并将我的功能粘贴在那里以帮助遇到类似问题的任何人?
猜你喜欢
  • 2011-03-13
  • 2021-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-17
  • 2011-03-06
相关资源
最近更新 更多