繁体   English   中英

在Perl中递归打印数据结构

[英]Recursively printing data structures in Perl

我目前正在学习Perl。 我有Perl哈希包含对哈希和数组的引用。 散列和数组可以依次包含对其他散列/数组的引用。

我写了一个子程序来递归地解析哈希并用适当的缩进打印它们。 虽然例程按预期工作,但我的导师并不相信下面代码的可读性和优雅性。

我非常感谢在这里获得Perl专家的观点,以便对下面的代码进行可能的优化。

这是我的完整代码片段..

# 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 == ARRAY(0x9d9baf8)
--------------------------------------------
    arrayofArrays[0] :
        arrayofArrays[0] :fred
        arrayofArrays[1] :barney
    arrayofArrays[1] :
        arrayofArrays[0] :george
        arrayofArrays[1] :jane
        arrayofArrays[2] :elroy
    arrayofArrays[2] :
        arrayofArrays[0] :homer
        arrayofArrays[1] :marge
        arrayofArrays[2] :bart


arrayofHash == ARRAY(0x9d9bba8)
--------------------------------------------
    arrayofHash[0] :
        husband == barney
        son == bamm bamm
        wife == betty
    arrayofHash[1] :
        husband == george
        son == elroy
        wife == jane


hashofHash == HASH(0x9da45f8)
--------------------------------------------

    flintstones == HASH(0x9d9bb48)
        husband == fred
        pal == barney

    jetsons == HASH(0x9d9bbf8)
        his boy == elroy
        husband == george
        wife == jane

    simpsons == HASH(0x9d9bc48)
        husband == homer
        kid == bart
        wife == marge
  1. 始终use strict ;
  2. 要成为一个好孩子,也要use warnings
  3. 用于子程序的名称应该使子程序的作用变得明显。 “recurseErrors”有点违反了这个原则。 是的,它确实递归。 但是有什么错误?
  4. 在每个子例程的第一行,您应该声明并初始化任何参数。 recurseErrors首先声明$ str然后声明其参数。
  5. 不要像在str()中那样混合shift和= @_
  6. 您可以考虑将现在称为recurseErrors的内容分解为用于处理数组和散列的专用例程。
  7. 没有必要像第99行和第109行那样引用变量。

除此之外,我认为你的导师那天过得很糟糕。

也许Data :: Dumper就是你想要的:

use Data::Dumper;

$str = Dumper($foo);
print($str);

如果您是perl的新手,我建议您通过perl-critic运行代码(还有一个可以从CPAN安装的脚本,通常我将它用作测试,因此无论何时我都可以从命令行运行“make make测试”)。 除了输出之外,您可能还需要更多地分解您的功能。 recurseErrors有三种情况可以拆分成子函数(甚至可以放入ref-type到子函数ref的散列)。

如果这是一个生产工作,我会使用Data :: Dumper ,但听起来这是家庭作业,所以你的老师可能不会太高兴。

以下是一个简单的示例,说明为什么您的代码不易读取:

$delimiter = "\n--------------------------------------------" unless (defined $delimiter);

您可以使用defined or运算符:

$delimiter //= "\n" . '-' x 44;

如果你担心早期的Perls:

defined $delimeter or $delimeter = "\n" . '-' x 44;

离开右边距的条件对我来说已经足够了,不能读取剩下的代码。

我的猜测是他不喜欢你

  1. 期望str函数中的哈希值。
  2. 调用相同的函数将数组作为哈希打印,尽管它们之间似乎没有共同的功能。
  3. 允许各种方式调用str ,但它从未计入最终结果。
  4. 允许可配置空间传递给根函数,但在递归函数中有一个硬编码选项卡。
  5. 省略实际在数组中占有一席之地的未定义值

这些是我能看到的问题,非常快。

您可以分离出处理数组和散列的代码块。

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 ) = @_;

(如果你这样做,那么Komodo至少可以弄清楚你的子程序的参数是什么)

几乎没有理由将变量放入仅包含变量的字符串中。

"$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};
}

my $object = My_Class->new;
my $string = "$object";

我之前一直在努力解决这个问题,并在这里找到了解决方法。 我几乎使用了这里发布的解决方案,但发现了一个更合适的解决方案(对我来说无论如何)。 在这里阅读Depth First Recursion

上述文章中的sub与包含其他Hashes,Arrays或Scalars的引用完美配合。 但它没有打印Hash密钥名称,所以我稍微修改了它:

#!/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 @_;
}
    #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 ; 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM