Tree::Simple::traverse() 没有访问树的根 - 错误或功能?
如果我尝试以下代码,
#!/usr/bin/env perl
use Tree::Simple;
# Tree:
# a
# _________ | ________
# / | \
# b c d
# / \
# e f
# \
# g
#
my $tree = Tree::Simple->new('a', Tree::Simple->ROOT);
$tree->addChildren( Tree::Simple->new('b'),
Tree::Simple->new('c'),
Tree::Simple->new('d'),
);
$tree->getChild(1)->addChildren (
Tree::Simple->new('e'),
Tree::Simple->new('f'),
);
$tree->getChild(1)->getChild(1)->addChildren (
Tree::Simple->new('g'),
);
$trav_func= sub {
my $node = shift;
printf "node : %s leaf : %3s root : %s\n",
$node->getNodeValue, $node->isLeaf ? 'yes' : 'no',
$node->isRoot ? 'yes' : 'no';
};
# traversal does not report the root - error ?
print "------ preorder : traverse( \$trav_func ) \n";
$tree->traverse( $trav_func );
print "\n";
print "------ postorder : traverse( sub{}, \$trav_func ) \n";
$tree->traverse( sub{}, $trav_func );
print "\n";
输出
------ preorder : traverse( $trav_func )
node : b leaf : yes root : no
node : c leaf : no root : no
node : e leaf : yes root : no
node : f leaf : no root : no
node : g leaf : yes root : no
node : d leaf : yes root : no
------ postorder : traverse( sub{}, $trav_func )
node : b leaf : yes root : no
node : e leaf : yes root : no
node : g leaf : yes root : no
node : f leaf : no root : no
node : c leaf : no root : no
node : d leaf : yes root : no
显示根“a”未被访问。我对树遍历的理解是所有节点都应该被访问。我是错的还是在某些情况下不访问根目录是有意义的?
附录:
Tree::Simple::traverse() 的实现如下:
sub traverse {
my ($self, $func, $post) = @_;
# ... some checks here not shown
foreach my $child ($self->getAllChildren()) {
$func->($child);
$child->traverse($func, $post);
defined($post) && $post->($child);
}
}
对于第一个节点(根),$func/$post 没有被调用,因此没有对其进行访问。
如果你用它重写 traverse() ,
package My::Tree::Simple;
use parent 'Tree::Simple';
# the original code of Tree::Simple::traverse()
# but $func() and $post() outside of foreach-loop
# allowing the root to be visited
sub my_traverse {
my ($self, $func, $post) = @_;
(defined($func)) || die "Insufficient Arguments : Cannot traverse without traversal function";
(ref($func) eq "CODE") || die "Incorrect Object Type : traversal function is not a function";
(ref($post) eq "CODE") || die "Incorrect Object Type : post traversal function is not a function"
if defined($post);
$func->($self); # put outside of foreach
foreach my $child ($self->getAllChildren()) {
$child->my_traverse($func, $post);
}
defined($post) && $post->($self); # put outside of foreach
}
它就会按我的预期工作。
if I try the following code
#!/usr/bin/env perl
use Tree::Simple;
# Tree:
# a
# _________ | ________
# / | \
# b c d
# / \
# e f
# \
# g
#
my $tree = Tree::Simple->new('a', Tree::Simple->ROOT);
$tree->addChildren( Tree::Simple->new('b'),
Tree::Simple->new('c'),
Tree::Simple->new('d'),
);
$tree->getChild(1)->addChildren (
Tree::Simple->new('e'),
Tree::Simple->new('f'),
);
$tree->getChild(1)->getChild(1)->addChildren (
Tree::Simple->new('g'),
);
$trav_func= sub {
my $node = shift;
printf "node : %s leaf : %3s root : %s\n",
$node->getNodeValue, $node->isLeaf ? 'yes' : 'no',
$node->isRoot ? 'yes' : 'no';
};
# traversal does not report the root - error ?
print "------ preorder : traverse( \$trav_func ) \n";
$tree->traverse( $trav_func );
print "\n";
print "------ postorder : traverse( sub{}, \$trav_func ) \n";
$tree->traverse( sub{}, $trav_func );
print "\n";
the output is
------ preorder : traverse( $trav_func )
node : b leaf : yes root : no
node : c leaf : no root : no
node : e leaf : yes root : no
node : f leaf : no root : no
node : g leaf : yes root : no
node : d leaf : yes root : no
------ postorder : traverse( sub{}, $trav_func )
node : b leaf : yes root : no
node : e leaf : yes root : no
node : g leaf : yes root : no
node : f leaf : no root : no
node : c leaf : no root : no
node : d leaf : yes root : no
showing that root 'a' is not visited. My understanding of tree traversal is that all nodes should be visited. Am I wrong or are there some cases where it makes sense not to visit the root ?
Appendix :
Tree::Simple::traverse() is implemented as :
sub traverse {
my ($self, $func, $post) = @_;
# ... some checks here not shown
foreach my $child ($self->getAllChildren()) {
$func->($child);
$child->traverse($func, $post);
defined($post) && $post->($child);
}
}
For the first node (root) $func/$post are not called, so there is no visit for it.
If you override traverse() with
package My::Tree::Simple;
use parent 'Tree::Simple';
# the original code of Tree::Simple::traverse()
# but $func() and $post() outside of foreach-loop
# allowing the root to be visited
sub my_traverse {
my ($self, $func, $post) = @_;
(defined($func)) || die "Insufficient Arguments : Cannot traverse without traversal function";
(ref($func) eq "CODE") || die "Incorrect Object Type : traversal function is not a function";
(ref($post) eq "CODE") || die "Incorrect Object Type : post traversal function is not a function"
if defined($post);
$func->($self); # put outside of foreach
foreach my $child ($self->getAllChildren()) {
$child->my_traverse($func, $post);
}
defined($post) && $post->($self); # put outside of foreach
}
it works as I expected.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我最近一直在使用 Tree::Simple 包,我认为观察到的行为与文档一致。例如,考虑“getDepth”函数。在文档中它说:
由此看来,在我看来,您需要“锚”,而“锚”不得包含数据。换句话说,你的树应该是这样的:
然后,'anchor' 的深度为 -1,'a' 的深度为 0,'b'、'c'、'd' 的深度为 1。
希望这样有帮助。
I've been recently using the Tree::Simple package and I think that the observed behavior is consistent with the documentation. Consider, for example, the 'getDepth' function. In the documentation it says:
From this, it seems to me that you need and "anchor" that must not contain data. In other words, your tree should look like this:
Then, the 'anchor' will be depth -1, 'a' will be depth 0, and 'b', 'c', 'd' will be depth 1.
Hope this helps.
你是对的。根应该包含在树遍历中。如果您尝试中序遍历,这一点尤其明显,因为(考虑到您的根有两个子节点)根应该位于两个子节点之间。谷歌一下,你会发现到处都是同样的行为。我什至检查了我的算法书。
我会确保我拥有该模块的最新版本,并将其报告为错误。
You are right. The root should be included in a tree traversal. This is especially clear if you try an inorder traversal, since (considering your root has two children) the root should be inbetween the two children. Google it, you'll see the same behavior everywhere. I even checked my book on algorithms.
I'd make sure i have the newest version of the module, and report it as an error.
所以,我同意这里所有的海报,这可能有点令人困惑。然而,该模块已经很成熟,并且目前正在许多地方/代码库中使用,因此我不会接受像改变 \&traverse 工作方式这样的激进行为改变。
也就是说,我当时的观点(现在仍然如此)认为,遍历(使用 \&traverse 方法实现)和访问之间是有区别的。如果您查看 Tree::Simple::Visitor 模块,您将能够通过相应地设置 \&includeTrunk 方法来完成您所追求的目标。此外,该模块中还有许多其他访问者实现,您可能会发现它们很有用。
我还鼓励您查看 Forest 模块,这是一个现代重写使用 Moose 的 Tree::Simple 。它也有一个以这种方式运行的 \&traverse 方法,但它还有一个 \&fmap_cont 方法,该方法更强大。此外 Forest 支持不可变(纯)树以及用于序列化/的默认读取器/写入器类反序列化你的树、索引你的树、JSON 序列化等等。
So, I agree with all the posters here, that this can be a little confusing. However, the module is well establish and is currently being used in a number of places/codebases so a radical behavior change like changing how \&traverse works is not something I would entertain.
That said, it was my view at the time (and kind of still is), that there is a difference between traversal (implemented with the \&traverse method) and visitation. If you take a look at the Tree::Simple::Visitor module, you will be able to accomplish exactly what you are are after by setting the \&includeTrunk method accordingly. Additionally there are a number of other visitor implementations in that module that you might find useful.
I would also encourage you to take a look at the Forest module, which is a modern re-write of Tree::Simple that uses Moose . It too has a \&traverse method which behaves in this way, but it also has a \&fmap_cont method which is much more powerful. Additionally Forest has support for immutable (pure) trees as well as default reader/writer classes for serializing/deserializing your trees, indexing your trees, JSON serialization and more.