在Perl中递归打印数据结构
我目前正在学习Perl。我有一个Perl哈希,它包含对哈希和数组的引用。散列和数组可能依次包含对其他散列/数组的引用 我编写了一个子例程来递归地解析散列,并用适当的缩进打印它们。虽然程序按预期工作,但我的指导老师对下面代码的可读性和优雅性并不确信 如果能在这里获得Perl专家对以下代码可能的优化的意见,我将不胜感激 这是我完整的代码片段在Perl中递归打印数据结构,perl,oop,recursion,Perl,Oop,Recursion,我目前正在学习Perl。我有一个Perl哈希,它包含对哈希和数组的引用。散列和数组可能依次包含对其他散列/数组的引用 我编写了一个子例程来递归地解析散列,并用适当的缩进打印它们。虽然程序按预期工作,但我的指导老师对下面代码的可读性和优雅性并不确信 如果能在这里获得Perl专家对以下代码可能的优化的意见,我将不胜感激 这是我完整的代码片段 # Array of Arrays $ref_to_AoA = [ [ "fred", "barney" ], [ "george", "j
# Array of Arrays
$ref_to_AoA = [
[ "fred", "barney" ],
[ "george", "jane", "elroy" ],
[ "homer", "marge", "bart" ],
];
#Array of Hashes
$ref_to_AoH = [
{
husband => "barney",
wife => "betty",
son => "bamm bamm",
},
{
husband => "george",
wife => "jane",
son => "elroy",
},
];
# Hash of Hashes
$ref_to_HoH = {
flintstones => {
husband => "fred",
pal => "barney",
},
jetsons => {
husband => "george",
wife => "jane",
"his boy" => "elroy", # Key quotes needed.
},
simpsons => {
husband => "homer",
wife => "marge",
kid => "bart",
},
};
# Hash which contains references to arrays and hashes
$finalHash = {
'arrayofArrays' => $ref_to_AoA,
'arrayofHash' => $ref_to_AoH,
'hashofHash' => $ref_to_HoH,
};
$string = str($finalHash);
print "$string\n";
#------------------------------------------------------------------
sub str {
my $hash = shift;
my ($space, $newline, $delimiter) = @_;
$space = "" unless (defined $space);
$newline = "\n\n\n" unless (defined $newline);
$delimiter = "\n--------------------------------------------" unless (defined $delimiter);
my $str = "";
for (sort keys %{$hash}) {
my $value = $hash->{$_};
$str .= "$newline$space$_ == $value$delimiter";
$str .= recurseErrors($value,$space);
}
$str;
}
#------------------------------------------------------------------
sub recurseErrors {
my $str;
my ($value,$space) = @_;
my $ref = ref $value;
if ($ref eq 'ARRAY') {
my $i = 0;
my $isEmpty = 1;
my @array = @$value;
$space .= "\t";
for my $a (@array) {
if (defined $a) {
$isEmpty = 0;
$str .= "\n$space$_\[$i\] :";
$str .= recurseErrors($a,$space);
}
$i++;
}
$str .= "= { }" if ($isEmpty);
} elsif ($ref eq 'HASH') {
$space .= "\t";
for my $k (sort keys %$value) {
if ( ( ref($value->{$k}) eq 'HASH') || (ref $value->{$k} eq 'ARRAY') ) {
my $val = $value->{$k};
$str .= "\n\n$space$k == ";
$str .= "$val";
}
else {
$str .= "\n$space$k == ";
}
$str .= recurseErrors($value->{$k},$space);
}
# we have reached a scalar (leaf)
} elsif ($ref eq '') {
$str .= "$value";
}
$str
}
#------------------------------------------------------------------
输出:
arrayofArrays==阵列(0x9d9baf8)
--------------------------------------------
阵列法拉利[0]:
阵列法拉利[0]:fred
阵列法拉利[1]:巴尼
阵列法拉利[1]:
阵列法拉利[0]:乔治
阵列法拉利[1]:简
阵列法拉利[2]:埃尔罗伊
阵列法拉利[2]:
阵列法拉利[0]:荷马
arrayofArrays[1]:marge
阵列法拉利[2]:巴特
arrayofHash==数组(0x9d9bba8)
--------------------------------------------
arrayofHash[0]:
丈夫=巴尼
son==bamm bamm
妻子=贝蒂
arrayofHash[1]:
丈夫=乔治
儿子=埃尔罗伊
妻子=简
hashofHash==散列(0x9da45f8)
--------------------------------------------
flintstones==散列(0x9d9bb48)
丈夫=弗雷德
帕尔=巴尼
jetsons==散列(0x9d9bbf8)
他的儿子埃尔罗伊
丈夫=乔治
妻子=简
辛普森一家==散列(0x9d9bc48)
丈夫=荷马
孩子=巴特
妻子=玛姬
也许这就是你想要的:
use Data::Dumper;
$str = Dumper($foo);
print($str);
下面是一个简单的示例,说明了代码不易阅读的原因:
$delimiter = "\n--------------------------------------------" unless (defined $delimiter);
您可以使用已定义或运算符:
$delimiter //= "\n" . '-' x 44;
如果您担心之前的错误:
defined $delimeter or $delimeter = "\n" . '-' x 44;
离开右边距的条件对于我来说已经足够让我不去阅读代码的其余部分了。如果您是perl新手,我建议您运行代码(还有一个脚本可以从CPAN安装,通常我将它用作测试,因此每当我执行“make test”时,它都会从命令行运行)。除了它的输出,您可能还想进一步分解函数。recurseErrors有三种情况可以拆分为子函数(甚至可以放入ref类型的散列中,以生成子函数ref)
如果这是一个制作工作,我会使用,但听起来这是家庭作业,所以你的老师可能不会太高兴
始终使用使用严格的李>
要想成为一个好孩子,还可以使用警告
子例程的名称应该使子例程的功能更加明显。“递归错误”有点违反了这个原则。是的,它会反复出现。但是什么错误呢
在每个子例程的第一行,您应该声明并初始化任何参数。recurseErrors首先声明$str,然后声明其参数李>
不要像在str()中那样混合使用shift和=@
<> LI>可以考虑将现在称为RealSebug的方法分解为处理数组和哈希的专用例程。
不需要像第99行和第109行那样引用变量
除此之外,我认为你的导师那天过得很糟糕。我猜他不喜欢你这样
在str
函数中需要一个散列李>
调用相同的函数将数组打印为哈希,尽管它们之间似乎没有公共函数
允许以各种方式调用str
,但它永远不会出现在最终结果中
允许将可配置空间传递给根函数,但在递归函数中有一个硬编码的选项卡李>
忽略在数组中实际占有位置的未定义值
我很快就能看到这些问题 您可以分离出处理数组和散列的代码块
sub recurse{
...
recurse_A(@_) if $ref eq 'ARRAY';
recurse_H(@_) if $ref eq 'HASH';
...
}
sub recurse_A{ ... }
sub recurse_H{ ... }
我建议你像这样开始你的子程序,除非你有很好的理由去做
sub example{
my( $one, $two, $three, $optional_four ) = @_;
(如果您这样做,那么至少能够找出子程序的参数)
很少有理由将变量放入只包含该变量的字符串中
"$var" eq $var;
我唯一能想到的是,当我使用一个具有重载“
函数的对象时,我想获取字符串,而不想同时获取对象
package My_Class;
use overload
'""' => 'Stringify',
;
sub new{
my( $class, $name ) = @_;
my $self = bless { name => $name }, $class;
return $self;
}
sub Stringify{
my( $self ) = @_;
return $self->{name};
}
我以前也遇到过同样的问题,并找到了解决办法。我几乎使用了一个发布在这里的解决方案,但找到了一个更适合我的。阅读关于深度优先递归的内容
上面文章中的sub与包含其他哈希、数组或标量的引用完美结合。不过,它没有打印哈希键名称,所以我对其进行了轻微修改:
#!/usr/bin/perl
#
# See:
#
# http://perldesignpatterns.com/?DepthFirstRecursion
#
use strict;
use warnings;
my %hash = (
'a' => {
'one' => 1111,
'two' => 222,
},
'b' => [ 'foo', 'bar' ],
'c' => 'test',
'd' => {
'states' => {
'virginia' => 'richmond',
'texas' => 'austin',
},
'planets' => [ 'venus','earth','mars' ],
'constellations' => ['orion','ursa major' ],
'galaxies' => {
'milky way' => 'barred spiral',
'm87' => 'elliptical',
},
},
);
&expand_references2(\%hash);
sub expand_references2 {
my $indenting = -1;
my $inner; $inner = sub {
my $ref = $_[0];
my $key = $_[1];
$indenting++;
if(ref $ref eq 'ARRAY'){
print ' ' x $indenting,'ARRAY:';
printf("%s\n",($key) ? $key : '');
$inner->($_) for @{$ref};
}elsif(ref $ref eq 'HASH'){
print ' ' x $indenting,'HASH:';
printf("%s\n",($key) ? $key : '');
for my $k(sort keys %{$ref}){
$inner->($ref->{$k},$k);
}
}else{
if($key){
print ' ' x $indenting,$key,' => ',$ref,"\n";
}else{
print ' ' x $indenting,$ref,"\n";
}
}
$indenting--;
};
$inner->($_) for @_;
}
我相信他的教练会对此感到不高兴。这是一套很好的规则。我们能让原始海报给我们更新一下他改进后的作业吗?我发现关于代码风格和设计的讨论非常有趣和有启发性。你的导师是个傻瓜。你的演示看起来不错。你确定你的讲师没有提到输出吗?
my $object = My_Class->new;
my $string = "$object";
#use strict ;
use warnings ;
# use module
use XML::Simple;
use Data::Dumper;
#debug print "START SCRIPT " ;
my $fileToParse = 'C:/Temp/CDIP/scripts/perl/nps_all_workflows.xml' ;
# create object
my $objXml= new XML::Simple;
# read XML file
my $data = $objXml->XMLin("$fileToParse");
# #debug print "\n FirstLevel is " . $objXml->{'POWERMART'} ;
my $level = 1 ;
#
printHashKeyValues ($data ) ;
sub printHashKeyValues
{
$level ++ ;
my $refHash = shift ;
my $parentKey = shift ;
my $parentValue = shift ;
while( my ($key, $value) = each %$refHash)
{
if ( defined ( $key ) )
{
if ( ref ($refHash->{"$key"}) eq 'HASH' )
{
my $newRefHash = $refHash->{"$key"} ;
#debug print " \n The key is a hash " ;
printHashKeyValues ($newRefHash , $key , $value) ;
}
if ( ref ($refHash->{"$key"}) eq 'ARRAY' )
{
#debug print " \n the key is an ARRAY " ;
printArrayValues ( $refHash->{"$key"} ) ;
}
} #eof if ( defined ( $key ))
if ( defined ( $value) )
{
if ( ref ($refHash->{"$value"}) eq 'HASH' )
{
my $newRefHash = $refHash->{"$value"} ;
#debug print " \n The value is a hash " ;
printHashKeyValues ($newRefHash , $key , $value) ;
}
if ( ref ($refHash->{"$value"}) eq 'ARRAY' )
{
#debug print " \n the value is an ARRAY " ;
printArrayValues ( $refHash->{"$value"} ) ;
}
} #eof if defined ( $value )
#debug print "\n key: $key, value: $value.\n";
} #eof while
} #eof sub
sub printArrayValues
{
my $arrRef = shift ;
my @array = @$arrRef;
my $parrentArrayElement = shift ;
#debug print "printArrayValues CALLED " ;
foreach my $arrayElement ( @array )
{
if (defined ( $arrayElement ) )
{
if ( ref ($arrayElement) eq 'HASH' )
{
#debug print " \n The \$arrayElement is a hash FROM THE ARRAY " ;
printHashKeyValues ($arrayElement ) ;
} #eof if
if ( ref ($arrayElement) eq 'ARRAY' )
{
#debug print " \n The \$arrayElement is a ARRAY FROM THE ARRAY " ;
printArrayValues ($arrayElement ) ;
} #eof if
#debug print "\n \$arrayElement is $arrayElement " ;
} #eof if ( defined ( $arrayElement ) )
} #eof foreach
} #eof sub
# #debug print output
##debug print Dumper($data);
1 ;
#!/usr/bin/perl
#
# See:
#
# http://perldesignpatterns.com/?DepthFirstRecursion
#
use strict;
use warnings;
my %hash = (
'a' => {
'one' => 1111,
'two' => 222,
},
'b' => [ 'foo', 'bar' ],
'c' => 'test',
'd' => {
'states' => {
'virginia' => 'richmond',
'texas' => 'austin',
},
'planets' => [ 'venus','earth','mars' ],
'constellations' => ['orion','ursa major' ],
'galaxies' => {
'milky way' => 'barred spiral',
'm87' => 'elliptical',
},
},
);
&expand_references2(\%hash);
sub expand_references2 {
my $indenting = -1;
my $inner; $inner = sub {
my $ref = $_[0];
my $key = $_[1];
$indenting++;
if(ref $ref eq 'ARRAY'){
print ' ' x $indenting,'ARRAY:';
printf("%s\n",($key) ? $key : '');
$inner->($_) for @{$ref};
}elsif(ref $ref eq 'HASH'){
print ' ' x $indenting,'HASH:';
printf("%s\n",($key) ? $key : '');
for my $k(sort keys %{$ref}){
$inner->($ref->{$k},$k);
}
}else{
if($key){
print ' ' x $indenting,$key,' => ',$ref,"\n";
}else{
print ' ' x $indenting,$ref,"\n";
}
}
$indenting--;
};
$inner->($_) for @_;
}