简体   繁体   English

在Perl中递归打印数据结构

[英]Recursively printing data structures in Perl

I am currently learning Perl. 我目前正在学习Perl。 I have Perl hash that contains references to hashes and arrays. 我有Perl哈希包含对哈希和数组的引用。 The hashes and arrays may in turn contain references to other hashes/arrays. 散列和数组可以依次包含对其他散列/数组的引用。

I wrote a subroutine to parse the hash recursively and print them with proper indentation. 我写了一个子程序来递归地解析哈希并用适当的缩进打印它们。 Though the routine works as expected, my instructor was not convinced about the readability and elegance of the below code. 虽然例程按预期工作,但我的导师并不相信下面代码的可读性和优雅性。

I would really appreciate to get the views of Perl experts here on possible optimization of the below code. 我非常感谢在这里获得Perl专家的观点,以便对下面的代码进行可能的优化。

Here is my complete code snippet.. 这是我的完整代码片段..

# 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
}
#------------------------------------------------------------------

Output: 输出:

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. Always use use strict ; 始终use strict ;
  2. To be a good boy, use use warnings as well. 要成为一个好孩子,也要use warnings
  3. The names you use for subroutines should make it obvious what the subroutine does. 用于子程序的名称应该使子程序的作用变得明显。 "recurseErrors" kind of violates that principle. “recurseErrors”有点违反了这个原则。 Yes, it does recurse. 是的,它确实递归。 But what errors? 但是有什么错误?
  4. On the first line of each subroutine you should declare and initialize any parameters. 在每个子例程的第一行,您应该声明并初始化任何参数。 recurseErrors first declares $str and then declares its parameters. recurseErrors首先声明$ str然后声明其参数。
  5. Don't mix shift and = @_ like you do in str() 不要像在str()中那样混合shift和= @_
  6. You might consider breaking up what is now called recurseErrors into specialized routines for handling arrays and hashes. 您可以考虑将现在称为recurseErrors的内容分解为用于处理数组和散列的专用例程。
  7. There's no need to quote variables like you do on lines 99 and 109. 没有必要像第99行和第109行那样引用变量。

Apart from that I think your instructor had a bad day that day. 除此之外,我认为你的导师那天过得很糟糕。

maybe Data::Dumper is what you want: 也许Data :: Dumper就是你想要的:

use Data::Dumper;

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

If you are new to perl, I'd recommend running your code through perl-critic (there is also a script you can install from CPAN, normally I use it as a test so it gets run from the command line whenever I do "make test"). 如果您是perl的新手,我建议您通过perl-critic运行代码(还有一个可以从CPAN安装的脚本,通常我将它用作测试,因此无论何时我都可以从命令行运行“make make测试”)。 In addition to its output, you might want to break up your functions a bit more. 除了输出之外,您可能还需要更多地分解您的功能。 recurseErrors has three cases that could be split into sub functions (or even put into a hash of ref-type to sub-function ref). recurseErrors有三种情况可以拆分成子函数(甚至可以放入ref-type到子函数ref的散列)。

If this were a production job, I'd use Data::Dumper , but it sounds like this is homework, so your teacher might not be too pleased. 如果这是一个生产工作,我会使用Data :: Dumper ,但听起来这是家庭作业,所以你的老师可能不会太高兴。

Here is one simple example why your code is not easily readable: 以下是一个简单的示例,说明为什么您的代码不易读取:

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

You could use the defined or operator: 您可以使用defined or运算符:

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

If you are worried about earlier Perls: 如果你担心早期的Perls:

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

Conditionals going off the right margin are enough of a turn-off for me not to read the rest of the code. 离开右边距的条件对我来说已经足够了,不能读取剩下的代码。

My guess is that he doesn't like that you 我的猜测是他不喜欢你

  1. expect a hash in the str function. 期望str函数中的哈希值。
  2. call the same function to print arrays as hashes, despite that there appears to be no common function between them. 调用相同的函数将数组作为哈希打印,尽管它们之间似乎没有共同的功能。
  3. allow various ways to call str , but it never figures into the final result. 允许各种方式调用str ,但它从未计入最终结果。
  4. allow configurable space to be passed in to the root function, but have a tab hardcoded in the recursive function. 允许可配置空间传递给根函数,但在递归函数中有一个硬编码选项卡。
  5. omit undefined values that actually hold a place in the arrays 省略实际在数组中占有一席之地的未定义值

Those are issues that I can see, pretty quickly. 这些是我能看到的问题,非常快。

You could have separated out the code blocks that dealt with arrays, and hashes. 您可以分离出处理数组和散列的代码块。

sub recurse{
  ...
  recurse_A(@_) if $ref eq 'ARRAY';
  recurse_H(@_) if $ref eq 'HASH';
  ...
}

sub recurse_A{ ... }
sub recurse_H{ ... }

I would recommend starting out your subroutines like this, unless you have a real good reason for doing otherwise. 我建议你开始这样的子程序,除非你有充分的理由不这样做。

sub example{
  my( $one, $two, $three, $optional_four ) = @_;

( If you do it like this then Komodo , at least, will be able to figure out what the arguments are to your subroutine ) (如果你这样做,那么Komodo至少可以弄清楚你的子程序的参数是什么)

There is rarely any reason to put a variable into a string containing only the variable. 几乎没有理由将变量放入仅包含变量的字符串中。

"$var" eq $var;

The only time I can think I would ever do that is when I am using an object that has an overloaded "" function, and I want to get the string, without also getting the object. 我能想到的唯一一次我会做的就是当我使用一个具有重载""函数的对象时,我想获取字符串,而不是获取对象。

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

I've struggled with this same problem before, and found my way here. 我之前一直在努力解决这个问题,并在这里找到了解决方法。 I almost used a solution posted here, but found a more suitable one (for me anyway). 我几乎使用了这里发布的解决方案,但发现了一个更合适的解决方案(对我来说无论如何)。 Read about Depth First Recursion here . 在这里阅读Depth First Recursion

The sub in the above article works perfectly with a reference containing other Hashes, Arrays, or Scalars. 上述文章中的sub与包含其他Hashes,Arrays或Scalars的引用完美配合。 It did not print Hash key names, though, so I slightly modified it: 但它没有打印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