arrays - 关于使用 Schwartzian 变换的 Perl 排序的建议
问题描述
我一直在看一篇关于在 Perl 中使用正则表达式对数组进行排序的旧帖子。原帖在这里
我正在努力完全理解被选为“正确”答案的脚本。原来的帖子是关于对下面的数组进行排序:
my @array = (
"2014 Computer Monitor 200",
"2010 Keyboard 30",
"2012 Keyboard 80",
"2011 Study Desk 100"
);
问题是如何在 perl 中使用正则表达式按年份、项目名称和价格对整个数组进行排序?例如,如果用户想按价格排序,他们输入“价格”,排序如下:
2010 Keyboard 30
2012 Keyboard 80
2011 Study Desk 100
2014 Computer Monitor 200
提出了一种使用 Schwartzian 变换的解决方案。我刚刚开始了解这一点,这个脚本与我见过的其他示例有点不同。被选为正确答案的脚本如下。我正在寻找有关它如何工作的建议。
my $order = "price";
my @array = (
"2014 Computer Monitor 200",
"2010 Keyboard 30",
"2012 Keyboard 80",
"2011 Study Desk 100"
);
my %sort_by = (
year => sub { $a->{year} <=> $b->{year} },
price => sub { $a->{price} <=> $b->{price} },
name => sub { $a->{name} cmp $b->{name} },
);
@array = sort {
local ($a, $b) = map {
my %h;
@h{qw(year name price)} = /(\d+) \s+ (.+) \s+ (\S+)/x;
\%h;
} ($a, $b);
$sort_by{$order}->();
} @array;
# S. transform
# @array =
# map { $_->{line} }
# sort { $sort_by{$order}->() }
# map {
# my %h = (line => $_);
# @h{qw(year name price)} = /(\d+) \s+ (.+) \s+ (\S+)/x;
# $h{name} ? \%h : ();
# } @array;
use Data::Dumper; print Dumper \@array;
我知道脚本使用正则表达式/(\d+) \s+ (.+) \s+ (\S+)/x
来匹配年份名称和价格。
我认为脚本的其余部分如下所示:
• 第 14 行的初始排序一次从@array 中获取两个项目,一个在 $a 中,一个在 $b 中
• map 函数然后获取项目$a 和$b 并将每个项目映射到一个散列——每个项目成为一个具有键'year'、'price' 和'name'的散列。这是基于正则表达式 /(\d+) \s+ (.+) \s+ (\S+)/x
• Map 返回两个哈希值,作为对局部变量 $a 和 $b 的引用
• 我认为有必要使用本地$a 和$b,否则排序将使用在第17 行排序开始时采用的默认$a 和$b?
• 'price' 排序函数作为 coderef 存储在 %sort_by 散列中
$sort_by{$order}->()
• 这在第 26 行由$a 和 $b 的本地版本的代码调用
重复此操作,直到所有项目都返回到第 14 行的 @array
请任何人告诉我我是否在正确的路线上,或者纠正任何误解。您还可以就本地 $a 和 $b 变量的使用提出建议。
谢谢J
解决方案
Schwartzian 变换是一种避免多次计算排序键的方法,就像在解决方案中一样 - 具有local ($a,$b)
S. 变换的步骤基本上是:
- 使用 Map 通过计算的排序键来丰富列表元素。在这里,
%h
被用作新元素,包含原行为line
- 使用 Sort 对这个富豪列表进行排序。
sort
带有一点肮脏的魔法$a $b
。 - 使用 Map 提取原始列表元素。这里通过提取
line
密钥。
关于$a $b
非常遗憾,$a
并且$b
是 Perl 中的全局变量。sort
它们通常在块内自动分配。像 sort { $a <=> $b } (3,2,1)
这解释了为什么 S. 解决方案有效,即使比较的元素没有作为排序子的参数给出。它还解释了需要local
(另一个 Perl 恐怖假装全局变量是本地的),所以天真的解决方案的排序函数在$a, $b
.
我强烈建议您忘记这一点并避免隐式使用 $a , $b 比排序块本身更深。
一个更容易理解的版本是:
my $order = "price";
my @array = (
"2014 Computer Monitor 200",
"2010 Keyboard 30",
"2012 Keyboard 80",
"2011 Study Desk 100"
);
my %sort_by = (
year => sub { shift->{year} <=> shift->{year} },
price => sub { shift->{price} <=> shift->{price} },
name => sub { shift->{name} cmp shift->{name} },
);
my @sorted =
map { $_->{line} }
sort { $sort_by{$order}->($a, $b) }
map {
my %h = (line => $_); # $_ is the array element (the input line)
@h{qw(year name price)} = ( $_ =~ /(\d+) \s+ (.+) \s+ (\S+)/x );
# Did the regex capture a name, i.e. did it work?
if( $h{name} ){
\%h
} else{
(); # Empty array will cause the invalid line to disappear, but you can choose to do something else with it.
}
} @array;
print(join("\n", @sorted))
推荐阅读
- python - 使用python从CSV中提取大量列和行?
- visual-studio-code - VSCode 动态 JSON 模式验证
- java - Springfox 文档 - 单独章节中的全局标题
- r - 从R中的一个变量转置另一个变量
- .net - 当在“for”循环中添加项目时,如何立即通知绑定到 ObservableCollection 的 DataGrid?
- javascript - 在 Javascript Daypilot 调度程序中重置“args”
- python - numpy.AxisError:轴 1 超出维度 1 数组的范围?
- ruby-on-rails - 从 rails 4.2.0 升级到 5.0.0 和 Ruby 从 2.2.4 到 2.5.8 - 自动加载常量 Sub 时检测到循环依赖
- mule - MuleSoft 的一般开发最佳实践是什么
- reactjs - SyntaxError:无法在模块 React JS Antd 之外使用 import 语句