排序变换思路:施瓦茨变换

施瓦茨变换(Schwartzian Transform)是一种排序思路。先看看它的结构:linux

my @output_data =
    map { EXTRACTION },
    sort { COMPARISON }
    map [ CONSTRUCTION ],
@input_data;

施瓦茨变换:app

  • construction:构造一个由原始数据以及被处理后准备用来作排序属性的元素组成的列表A
  • comparison:从列表A中取得元素进行排序,获得排序后列表B
  • expraction:从列表B中取得原始元素获得新列表C
  • 列表C就是期待的排序结果

先举个例子,文件test.txt中的内容以下:函数

1     mac     2000    500
2     winxp   4000    300
3     bsd     1000    600
4     linux   1000    200
5     SUSE    4000    300
6     Debian  600     200

如今须要使用perl对该文件进行排序,以第三字段为主排序依据(升序),第四字段(升序)、第一字段(降序)分别为辅助排序依据。性能

下面这种代码是谁都会的:优化

open DATA,"<","/perlapp/test.txt"
    or die "Can't open file: $!";

print sort {
                my @x = split / +/,$a;
                my @y = split / +/,$b;
                $x[2] <=> $y[2] 
                or $x[3] <=> $y[3]
                or $y[0] <=> $x[0]
           } <DATA>;

上面的排序过程当中,对每一行都进行了一次split函数处理,换句话说,每一次比较操做都进行了两次split。code

使用施瓦茨变换,能够将每次比较过程当中每一种函数的屡次操做都减小为一次,正如上面的split能够减小为一次(性能并无更优化,只是代码减小了,更漂亮了)。orm

如下是使用Schwartzian Transform实现上述排序需求的代码:排序

open DATA,"<","/perlapp/test.txt" or die "Can't open file: $!";

print map { $_->[0] }
      sort { 
          $a->[3] <=> $b->[3] 
          or $a->[2] <=> $b->[2] 
          or $b->[1] <=> $a->[1] }
      map { [ $_,split / +/,$_ ] } <DATA>;

在上面施瓦茨变换代码中(从下往上看):input

  • 最后一个map函数,将<DATA>中的元素从新构建成了一个新的匿名列表A,这个列表中除了原始文件中的每一行数据,还有对每一行进行split后的元素,由于每一行都是空格分隔的,因此split后的每个元素都直接做为匿名列表的元素。大概以下:["6 Debian 600 200","6","Debian","600","200"]
  • sort函数对匿名列表A进行排序,取列表A中须要排序的元素进行排序,排序后获得一个新的排序列表B,这个列表中仍然包含了原始数据和split后的各元素,只不过它们是通过排序后的
  • 第一个map函数从排序后的列表B中取出第一个元素,也就是文件中的元素数据,获得最终的排序列表C
相关文章
相关标签/搜索