使用Schwartzian变换对数据进行排序

如果您完全不熟悉使用Perl对数据进行排序,请在阅读本文之前,阅读“使用Perl排序数据-第一部分和第二部分”文章。 入门的Perl编码人员可能会发现本文使用了不熟悉的术语和语法。 中级和高级Perl编码人员应该发现本文有用。 本文的目的是告知读者,它不是关于如何编写Perl或如何编写良好的Perl代码,而是以一种希望容易理解的方式教授Schwartzian变换技术。

Schwartzian变换

Schwartzian转换(从现在开始称为ST)在实现和效率之间取得了很好的平衡,使其成为数据排序的有吸引力的选择。 出于好奇,此技术的名称是为了纪念其创始人Randal Schwartz。 本文详细介绍了ST的工作原理。

ACME INC。

首先,我们需要一些数据进行排序。 本文末尾附有一个数据文件(employees.txt)。 这是一个由制表符分隔的ACME INC员工价值列表。它是10名雇员的列表,其中某些字段描述了其在ACME INC的就业状况。这些字段是:

员工编号,姓名,雇用日期,职位,部门,薪水


EmployeeID    Name    HireDate    Position    Department    Salary
M011    Smith,John,T    01-12-1981    Welder    Maintenance    20.00 p/h
M102    Hart,Thomas,J    06-30-1982    Supervisor    Maintenance    28.50 p/h
J309    Jones,Steve,W    02-23-1990    Janitor    Janitorial    15.50 p/h
A124    White,Mary,H    03-15-1990    Assembler    Assembly    8.75 p/h
Q365    Miles,Frank,R    12-01-1999    Inspector    Quality    16.25 p/h
A316    Roberts,Andy,P    07-30-2006    Assembler    Assembly    8.00 p/h
S554    William,Terry,K    03-3-2005    Expeditor    Stock Room    9.00 p/h
R078    Norris,Chris,K    04-17-2003    Clerk    Shipping/Recieving    9.00 p/h
X832    Anderson,Jane,M    03-23-1992    VP Operations    Mangement    33.00 p/h
X111    Kelly,Mark,D    05-29-1989    Engineer    Engineering    26.75 p/h 
数据文件将被读入哈希哈希中(如果您不了解哈希哈希是什么,请参阅本文末尾的“在线资源”部分)。

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper; # for printing a "picture" of the data
my %employees;
open (my $in, 'path/to/employees.txt') or die "$!";
readline <$in>; #skip file header
while (<$in>) {
    chomp;
    my ($id,$name,$hdate,$pos,$dept,$sal) = split/\t/;
    $employees{$id} = {NAME => $name, HIREDATE => $hdate, POSITION => $pos, DEPT => $dept, SALARY => $sal};
}
close $in;
print Dumper \%employees; 
上面的代码从employees文件创建一个哈希哈希,将EmplyeeID用作顶级哈希键。 它会产生一系列这样的记录(Data :: Dumper的部分输出):

'R078' => {
           'DEPT' => 'Shipping/Recieving',
           'POSITION' => 'Clerk',
           'NAME' => 'Norris,Chris,K',
           'HIREDATE' => '04-17-2003',
           'SALARY' => '9.00 p/h'
          }, 
“ R078”(员工ID)是匿名哈希的密钥(如果您不熟悉匿名哈希的含义,请参见本文结尾的参考资料部分),该员工哈希数据存储在键/值对中。 HIREDATE是MM-DD-YYYY的美国格式。 我们将按雇用日期升序对记录进行排序。 为此,我们需要将日期填入可排序的键中。

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %employees;
open (my $in, 'path/to/employees.txt') or die "$!";
readline <$in>; #skip header
while (<$in>) {
    chomp;
    my ($id,$name,$hdate,$pos,$dept,$sal) = split/\t/;
    $employees{$id} = {NAME => $name, HIREDATE => $hdate, POSITION => $pos, DEPT => $dept, SALARY => $sal};
}
close $in;
my @sorted = map  {$employees{$_->[0]}{NAME} was hired on $employees{$_->[0]}{HIREDATE}}
             sort {$a->[1] cmp $b->[1]}
             map  {my ($m,$d,$y) = split/-/,$employees{$_}{HIREDATE};
                  [$_, "$y$m$d"] } keys %employees; 
print Dumper \@sorted; 
我们最终得到的是按雇用日期排序的员工列表:

“史密斯,约翰,T于1981年1月12日被雇用”,

'Hart,Thomas,J在1982年6月30日被聘用',

“ Kelly,Mark,D于1989年5月29日被雇用”,

“琼斯,史蒂夫,W于1990年2月23日被聘用”,

“ White,Mary,H于1990年3月15日被聘用”,

'Anderson,Jane,M于1992年3月23日被聘用',

“ Miles,Frank,R于1999年12月1日被雇用”,

“ Norris,Chris,K于2003年4月17日被雇用”,

“威廉,特里,K于2005年3月3日被雇用”,

“罗伯茨,安迪,P于2006年7月30日被雇用”

让我们仔细看一下ST,将大部分代码剥离掉以揭示正在发生的事情。


my @sorted = map  {}
             sort {}
             map  {} keys %employees;  
这就是ST的基本要点:一个包装在两个map {}块中的排序块。 是什么让Schwartzian转型飞

使ST只需很少的代码且没有临时变量的原因是能够将列表处理功能和Perls支持的匿名数据存储链接在一起。

线性可视化ST技术可能更容易。 作为流程图,它看起来像带有指示输入/输出方向的箭头:


END output  <-- map {} <-- sort {} <-- map {} <-- input START; 
使用此技术时,第一个地图块实际上是最棘手的部分。 在该块中,您可以决定创建排序块将要排序的排序键列表所需的操作。 让我们仔细看看。 地图功能

映射块类似于子例程:

map  {
    my ($m,$d,$y) = split/-/,$employees{$_}{HIREDATE};
    [$_, "$y$m$d"]
 } keys %employees; 
数据从追加到末尾的列表中读取到映射块中,而不是像子例程一样通过@_导入。 该列表可以是任何列表:数组,哈希,(事物列表),另一个创建列表的函数(例如grep()和split()函数),文件句柄,甚至是返回列表的子例程。

映射块也非常像一个循环,但与for / foreach / while / until循环不同。 Map将像grep()一样处理列表的所有元素。 您不能在地图上使用“上一个”,“下一个”或“重做”之类的循环控件。

在我们的示例中,我们仅使用一个比较来对数据进行排序:

sort {$a->[1] cmp $b->[1]}
但是,您可以根据需要使用多个比较来使用||对数据进行排序。 (或)运算符:
sort {$a->[1] cmp $b->[1] || 
            $b->[2] <=> $a->[2] ||
            $a->[3] cmp $b->[3]}
请注意,“ cmp”和“ <=>”的混合使用以及升序和降序都在同一排序块中进行。 这是完全有效的,并且根据您的特定需求非常有用。

最后一个映射块也可以调整最终输出,但是您需要将其用于特定的应用程序。 在我们的示例中,我们仅添加了一些文本以使输出更具可读性。 但是您可以在该map块中做任何您想做的事情:忽略一些数据,添加一些标记代码(例如html代码),更改字段的顺序,无论您想要什么。

为什么要使用施瓦兹变换?

当排序键实际上不存在于数据中并且排序键仅用于对数据进行排序而不用于任何其他目的时,ST是一个很好的选择。 就像在我的示例中一样,我们按雇用日期对列表进行排序,但是排序键必须通过即时拆分日期并将MM-DD-YYYY的美式日期重新排序为可排序的ASCII字符串YYYYMMDD来进行。 该日期可以按数字排序,但将其作为字符串排序确实更容易。

ST的另一个好用法是,您必须对一些数据进行排序,以测试将在更大更健壮的程序中使用的某些排序键时。

为什么不使用Schwartzian变换?

如果您的数据中的键已经是可排序的格式,则无需使用ST技术,但如果需要,您仍然可以。 您可能还考虑使用一种数据库,该数据库比Perl脚本可以更有效地对数据进行排序。

结论

Schwartzian转换是一种强大而方便的数据排序方法。 如果在阅读本文后仍然感到困惑,请尝试尝试一些代码以了解其工作原理。 一旦掌握了这项技术,您将发现自己一直在使用它。

凯文(又名KevinADC)

在线资源 Perldoc网站所有在线Perl文档。 perlreftut -马克的有关引用非常简短的教程perldsc -数据结构复杂的数据结构结构perllol -在Perl数组的操作数组地图 -将更改应用于列表找回与变化的新名单

本文受《

创用CC许可
附加的文件
档案类型:txt employee.txt (667字节,501次浏览)

From: https://bytes.com/topic/perl/insights/775438-sorting-data-schwartzian-transform

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值