在Perl中递归打印数据结构

时间:2009-06-24 13:16:59

标签: perl oop recursion

我目前正在学习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

8 个答案:

答案 0 :(得分:8)

  1. 始终使用use strict;
  2. 要成为一个好孩子,也可以使用use warnings
  3. 您用于子程序的名称应该明确子程序的作用。 “recurseErrors”有点违反了这个原则。是的,它确实递归。但是有什么错误?
  4. 在每个子程序的第一行,您应该声明并初始化任何参数。 recurseErrors首先声明$ str然后声明其参数。
  5. 不要像在str()
  6. 中那样混合shift和= @_
  7. 您可以考虑将现在称为recurseErrors的内容分解为用于处理数组和哈希的专用例程。
  8. 没有必要像99号线和109号那样引用变量。
  9. 除此之外,我认为你的导师那天过得不好。

答案 1 :(得分:7)

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

use Data::Dumper;

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

答案 2 :(得分:6)

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

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

答案 3 :(得分:3)

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

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

您可以使用defined or运算符:

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

如果你担心早期的Perls:

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

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

答案 4 :(得分:1)

我的猜测是他不喜欢你

  1. 期望str函数中的哈希值。
  2. 调用相同的函数将数组打印为哈希,尽管它们之间似乎没有共同的功能。
  3. 允许以各种方式调用str,但它从未计入最终结果。
  4. 允许将可配置空间传递给根函数,但在递归函数中使用硬编码选项卡。
  5. 省略实际在数组中占有位置的未定义值
  6. 这些是我能够很快看到的问题。

答案 5 :(得分:1)

你可以分离掉处理数组和哈希的代码块。

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

答案 6 :(得分:1)

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

上述文章中的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 @_;
}

答案 7 :(得分:0)