在保留顺序的同时按值长度对散列进行排序

时间:2018-09-17 11:25:06

标签: perl sorting hash key

我当前正在编写一个Perl脚本,以按标准输入对行进行排序,并按行长的顺序打印行,同时保留相等的行的顺序。 我的排序代码包含以下内容:

while (my $curr_line = <STDIN>) {
    chomp($curr_line);
    $lines{$curr_line} = length $curr_line;
}

for my $line (sort{ $lines{$a} <=> $lines{$b} } keys %lines){
    print $line, "\n";
}

例如,我的标准输入包含以下内容:

tiny line
medium line
big line
huge line
rand line
megahugegigantic line

我将得到以下输出:

big line
rand line
tiny line
huge line
medium line
megahugegigantic line

我有什么办法可以保留等长线的顺序,以使细线先于大字而先于兰特?另外,每次我运行脚本时,顺序似乎都会改变。

预先感谢

4 个答案:

答案 0 :(得分:3)

一种可能的解决方案

您可以将行的位置以及length保存在输入文件句柄中。 $. magic variable (input line number)提供了这一点。然后,您可以对两个值进行排序。

use strict;
use warnings;

my %lines;
while ( my $curr_line = <DATA> ) {
  chomp($curr_line);
  $lines{$curr_line} = [ length $curr_line, $. ];
}

for my $line (
  sort {
       $lines{$a}->[0] <=> $lines{$b}->[0]
    || $lines{$a}->[1] <=> $lines{$b}->[1]
  } keys %lines
) {
  print $line, "\n";
}

__DATA__
tiny lin1
medium line
big line
huge lin2
rand lin3
megahugegigantic line

这将始终输出

big line
tiny lin1
huge lin2
rand lin3
medium line
megahugegigantic line

您当然也可以使用哈希使代码更具可读性。

$lines{$curr_line} = { 
  length   => length $curr_line, 
  position => $., 
};

您的实现说明

由于随机哈希排序,您的结果每次都会更改其顺序。由于Perl实现散列的方式,keys返回键列表的方式是随机的。这是设计使然,并且具有安全性功能。由于存在多个具有相同值的键,因此排序有时会返回不同的结果,具体取决于哪个等值键首先出现。

您可以通过在通话sort之前附加另一个keys来减轻这种情况。这样就可以按名称对键进行排序,至少可以使不需要的结果的顺序保持一致。

#                                               vvvv
for my $line (sort{ $lines{$a} <=> $lines{$b} } sort keys %lines) { ... }

请注意,如果在\n时放回print,则不必chomp输入。无论如何,它总是一样长。如果这样做,您应该print $/chomp被删除的input record separator)中伪造数据,或者您伪造数据。

答案 1 :(得分:2)

排序不是问题,Perl使用了稳定排序的快速排序算法,匹配相同排序键的输入在排序输出上的顺序与输入的顺序相同。

您的问题是您将这些行存储在哈希中。哈希是键值对的无序集合,因此将行添加到哈希中,然后再次将它们打印出来而不进行排序将使您以随机顺序获得行。

您需要将所有行读入数组,然后按长度排序,最快的方法是使用Schwartzian Transformation,请参见下文。

my  @lines = <STDIN>;
chomp(@lines);

my @sorted = # This is the clever bit and needs to be red from the last map up
    map { $_->[0] } # Get the lines
    sort { $a->[1] <=> $b->[1] }  # Sort on length
    map { [$_, length $_] } # Create a list of array refs containing
        # the line and the length of the line
    @lines;

print join "\n", @sorted; # print out the sorted lines

答案 2 :(得分:2)

您无法在任何地方存储原始订单,因此无法对其进行排序。最简单的解决方法是将行存储在数组中,并确保Perl使用稳定的排序。

use sort 'stable';

my @lines = <>;
chomp(@lines);

for my $line ( sort { length($a) <=> length($b) } @lines) {
   say $line;
}

[ ST在这方面过大了。太夸张了,它甚至可能使速度变慢!]

答案 3 :(得分:1)

正如已经解释的那样,随机性来自于您使用哈希键存储字符串。不需要这个,或者像 Schwartzian变换这样更复杂的东西来完成这项工作

v5.8以后的所有Perl版本都使用了 stable 排序,它将保留按相同顺序均等排序的值。但是您可以坚持使用sort杂用语和{p>

sort

这是我编写程序的方式。它会在文件末尾停止读取输入,或者在您希望从键盘输入数据的情况下看到空白行时停止读取

use sort 'stable'

使用与您在问题中使用的输入相同的输入,会产生

输出

use strict;
use warnings 'all';
use feature 'say';
use sort 'stable';

my @list;

while ( <> ) {
    last unless /\S/;
    chomp;
    push @list, $_;
}

say for sort { length $a <=> length $b } @list;