请你帮我解决问题的逻辑。
我有三个文件:
mydata1
mydata2
mydata1###parent
mydata2###parent2
parent###child1
parent###child2
parent###child3
child1###subchild1
child1###subchild2
child1###subchild3
subchild1###Ssubchild1
subchild1###Ssubchild2
subchild1###Ssubchild3
我需要一个包含所有根项目的列表以及每个子项目(任何两个或多个跳过根目录的项目)以及到达那里的路径。请注意,mydata2
和parent2
未显示在下方,因为它们没有子项。
看起来像这样
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
答案 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);
}
}