问题描述
我对 perl 很陌生。我正在尝试使用来自 CPAN 的以下代码。
my $C;
# Recursive version of C<each>;
sub reach {
my $ref = shift;
if (ref $ref eq 'HASH') {
if (defined $C->{$ref}{v}) {
if (ref $C->{$ref}{v} eq 'HASH') {
if (my @rec = reach($C->{$ref}{v})) {
return ($C->{$ref}{k},@rec);
}
} elsif (ref $C->{$ref}{v} eq 'ARRAY') {
if (my @rec = reach($C->{$ref}{v})) {
if (defined $C->{$ref}{k}) {
return $C->{$ref}{k},@rec;
}
return @rec;
}
}
undef $C->{$ref};
}
if (my ($k,$v) = each %$ref) {
$C->{$ref}{v} = $v;
$C->{$ref}{k} = $k;
return ($k,reach($v));
}
return ();
} elsif (ref $ref eq 'ARRAY') {
if (defined $C->{$ref}{v}) {
if (ref $C->{$ref}{v} eq 'HASH' ||
ref $C->{$ref}{v} eq 'ARRAY') {
if (my @rec = reach($C->{$ref}{v})) {
if (defined $C->{$ref}{k}) {
return $C->{$ref}{k},@rec;
}
return @rec;
}
}
}
if (my $v = $ref->[$C->{$ref}{i}++ || 0]) {
$C->{$ref}{v} = $v;
return (reach($v));
}
return ();
}
return $ref;
}
输入:
bar => {cmd_opts => { gld_upf => ['abc','def']} }
电流输出:
[bar,cmd_opts,gld_upf,abc]
[bar,def]
期望输出:
[bar,['abc','def']]
另外,这段代码中使用了哪些概念? 有什么书/课程我可以学习吗?
解决方法
另外,这段代码中使用了哪些概念?有什么书/课程我可以学习吗?
您在Deep::Hash::Utils
CPAN 模块中提到的代码主要是处理嵌套数据结构。
有几个地方可以阅读这些内容:
- 官方文档:perldsc; perlreftut; perlref;
- Modern Perl by chrom 有一个关于嵌套数据结构的部分,大约在第 60 页
- Intermediate Perl: Beyond The Basics of Learning Perl 2nd edition 在第 44 页有一节关于嵌套数据结构。
在最基本的情况下,在这些嵌套数据结构中,每个节点都具有以下类型之一:
- 标量
- 哈希引用
- 数组引用
反过来,arrayref 指向的数组中的值可以是 scalar/hashref/arrayref 类型。 arrayref 指向的哈希值也是如此,它可以是 scalar/hashref/arrayref 类型。
这会产生一个 tree-like 结构。遍历这样一棵树的算法是depth-first search 其中需要一些额外的逻辑来检查节点的类型,并根据类型决定如何进一步向下处理树。
为了进行平行,所有这些与遍历文件系统层次结构并没有太大区别(参见link1、link2)。
关于 Perl 资源的 bigger list called perlres 可用。
在这种特定情况下,Deep::Hash::Utils
中的函数 reach
充当迭代器,它返回从根向下到每个叶子的所有路径。
每当找到叶子的 @path
时,它的元素都会与另一个名为 @output
的列表并排比较,并且存在三种情况:
- 那个位置没有元素,所以我们存储它
- 元素是相等的,所以我们跳过它们
- 元素不同,所以我们将它们合并到一个列表中
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use Deep::Hash::Utils qw/reach/;
my $input = { bar => {cmd_opts => { gld_upf => ['abc','def']} } };
my @output = ();
while (my @path = reach($input)) {
for(my $i=0;$i<=$#path;$i++){
if(defined $output[$i]) {
if(ref($output[$i]) eq "") {
if($output[$i] eq $path[$i]) {
next;
};
my $e1 = $output[$i];
my $e2 = $path[$i];
$output[$i] = [$e1,$e2];
}elsif(ref($output[$i]) eq "ARRAY"){
push @{$output[$i]},$path[$i];
};
} else {
$output[$i] = $path[$i];
};
};
}
print Dumper \@output;
输出:
$VAR1 = [
'bar','cmd_opts','gld_upf',[
'abc','def'
]
];