步行树结构并枚举所有路径

时间:2016-07-17 08:47:39

标签: perl

请你帮我解决问题的逻辑。

我有三个文件:

data.txt中

mydata1
mydata2

parent.txt

mydata1###parent
mydata2###parent2

child.txt

parent###child1
parent###child2
parent###child3
child1###subchild1
child1###subchild2
child1###subchild3
subchild1###Ssubchild1
subchild1###Ssubchild2
subchild1###Ssubchild3

我需要一个包含所有根项目的列表以及每个子项目(任何两个或多个跳过根目录的项目)以及到达那里的路径。请注意,mydata2parent2未显示在下方,因为它们没有子项。

看起来像这样

mydata1 === child1 === parent + child1
mydata1 === subchild1 === parent + child1 + subchild1
mydata1 === Ssubchild1 === parent + child1 + subchild1 + Ssubchild1
mydata1 === Ssubchild2 === parent + child1 + subchild1 + Ssubchild2
mydata1 === Ssubchild3 === parent + child1 + subchild1 + Ssubchild3
mydata1 === subchild2 === parent + child1 + subchild2
mydata1 === subchild3 === parent + child1 + subchild3
mydata1 === child2 === parent + child2
mydata1 === child3 === parent + child3

我用以下逻辑尝试了这个问题,但没有得到预期的解决方案。

#!/usr/bin/perl

use strict;

my @host_name = ("mydata1","mydata2");
my %parent_hash;
my %child_hash;

open (parent,"<","parent.txt") or die "could not open\n";
open (child,"<","childs.txt") or die "could not open\n";

for(<parent>)
{
    my @arr = split("###",$_);
    $parent_hash{$arr[0]}{$arr[1]} = 1;
}

for(<child>)
{
    my @arrr = split("###",$_);
    $child_hash{$arrr[0]}{$arrr[1]} = 1;
}

my $parent;

for(@host_name)
{
    my $host = $_; 

    if(exists($parent_hash{$host}))
    {
        for (keys %{$parent_hash{$host}})
        {
            chomp($_);
            $parent = $_;
            print "$host === $parent ==== $parent\n";
            derive($host,$_,$_,$parent);
        }
    }
}


sub derive()
{
    my $host = shift;
    my $group = shift;
    my $string = shift;
    my $parent = shift;




    for (keys %{$child_hash{$group}})
        {
            chomp($_);
            my $temp = $string;
            $string = $string."+".$_;

            if(exists ($child_hash{$_}))
            {
                print "$host === $_ ==== $string\n";
                derive($host,$_,$string,$parent);
            }   
            else
            {
                print "$host === $_ ==== $string\n";
                $string = $temp;    
            }
        }

}

并得到以下结果而不是预期的结果

mydata1 === parent ==== parent  
mydata1 === child1 ==== parent+child1
mydata1 === subchild3 ==== parent+child1+subchild3
mydata1 === subchild2 ==== parent+child1+subchild2
mydata1 === subchild1 ==== parent+child1+subchild1
mydata1 === Ssubchild2 ==== parent+child1+subchild1+Ssubchild2
mydata1 === Ssubchild3 ==== parent+child1+subchild1+Ssubchild3
mydata1 === Ssubchild1 ==== parent+child1+subchild1+Ssubchild1
mydata1 === child2 ==== parent+child1+child2
mydata1 === child3 ==== parent+child1+child3
mydata2 === parent2 ==== parent2

1 个答案:

答案 0 :(得分:1)

您的解决方案似乎工作正常,除非您没有对树的子项进行排序,并且您的循环中有一条线迭代打印出父项的主机名。否则我看不出你的输出有任何区别。

因此,更改将删除该print语句并将for (keys %{$child_hash{$group}})更改为for (sort keys %{$child_hash{$group}})

但是我也完成了整个程序并修复了存储数据的方式,以便更加自然,并解释了一半你正在使用的perl功能。

use strict;
use warnings;

# Rather than explicitly managing parents and children, you can quite literally store the whole tree
# in memory as a tree for more native iteration through it. For initialization you also need a
# global node list so you can do the lookup.
my @host_names;
my %tree;
my %nodes;

# You can use scalar variables ($fh) instead of global names (fh) for filehandles. This means that
# they will automatically close when they go out of scope if they are used in functions or loops and
# will be checked for existance by use strict which is great for me personally because I make
# variable name typos all the time.
open(my $parent,"<","parent.txt") or die "Could not open 'parent.txt' for read: $!";
open(my $child,"<","child.txt") or die "Could not open 'child.txt' for read: $!";

# As an example of that, this do block will read the host file into host_names and then
# automatically close the file once the block has exited.
@host_names = do {
  open(my $host, '<', 'data.txt') or die "Could not open 'data.txt' for read: $!";
  <$host>;
};
chomp(@host_names);

foreach my $host_name (@host_names) {
  $tree{$host_name} = $nodes{$host_name} = {};
}

# In the ultimate confusing thing that you only have to learn once, you actually want to use while
# instead of for to loop through files. Using a for loop will read the whole file into memory, split
# it on newline and then iterate through it.  While loops will read the file one line at a time.  It
# doesn't make a difference here, but if you were reading a 3GB file, you would notice.
while(<$parent>) {
  chomp;
  # You can automatically unpack arrays in perl and the default second argument for split is $_
  my ($parent, $child) = split(/###/);
  die "$parent does not exist" unless exists $nodes{$parent};
  die "$child already defined" if     exists $nodes{$child};
  $nodes{$parent}{$child} = $nodes{$child} = {};
}

# This loop is exactly the same as above now, you could really make the two files the same and see
# no differences (except that I changed split invocations to give you some more information)
while(<$child>) {
  chomp;
  # If you want, you can also specify the maximum number of items split will look to unpack. If it's
  # not specified and perl sees you unpack it immediately, it will default to the number of items in
  # your list plus 1 (in your case 3). It does this because the behavior is the same either way, and
  # it saves work if you have 200 splittable things in your line. We know there will be exactly two
  # items, so we can save it even more work and tell it that.
  my ($parent, $child) = split(/###/, $_, 2);
  die "$parent does not exist" unless exists $nodes{$parent};
  die "$child already defined" if     exists $nodes{$child};
  $nodes{$parent}{$child} = $nodes{$child} = {};
}

# You can specify what you're iterating over in a for/foreach loop (They are synonyms in perl)
# instead of renaming it on the first line of the loop.
foreach my $host (@host_names) {
  walk($tree{$host}, $host);
}

# With our tree now actually being a tree in memory, our 'derive' function can be a true tree walk.
sub walk {
  my ($node, $host, @path) = @_;
  for my $child (sort keys %$node) {
    if (@path > 0) {
      local $" = " + "; # comment to fix stack overflow highlighter "
      print "$host === $child ==== @path + $child\n";
    }
    walk($node->{$child}, $host, @path, $child);
  }
}