Tree::Simple::traverse() 没有访问树的根 - 错误或功能?

发布于 2024-12-08 15:47:12 字数 3138 浏览 0 评论 0原文

如果我尝试以下代码,

#!/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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

赠我空喜 2024-12-15 15:47:12

我最近一直在使用 Tree::Simple 包,我认为观察到的行为与文档一致。例如,考虑“getDepth”函数。在文档中它说:

获取深度
返回一个数字,表示调用者在范围内的深度
Tree::Simple 对象的层次结构。

注意:ROOT 树的深度为 -1。这是因为 Tree::Simple
假设树的根通常不包含数据,而只是
包含数据的分支的锚点。这可能不直观
在所有情况下,所以我在这里提到它。

由此看来,在我看来,您需要“锚”,而“锚”不得包含数据。换句话说,你的树应该是这样的:

# Tree:
#                 anchor
#                   | 
#                   a
#         _________ | ________
#        /          |         \ 
#       b           c          d 
#                 /    \
#                e      f  
#                        \
#                         g
#

然后,'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:

getDepth
Returns a number representing the invocant's depth within the
hierarchy of Tree::Simple objects.

NOTE: A ROOT tree has the depth of -1. This be because Tree::Simple
assumes that a tree's root will usually not contain data, but just be
an anchor for the data-containing branches. This may not be intuitive
in all cases, so I mention it here.

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:

# Tree:
#                 anchor
#                   | 
#                   a
#         _________ | ________
#        /          |         \ 
#       b           c          d 
#                 /    \
#                e      f  
#                        \
#                         g
#

Then, the 'anchor' will be depth -1, 'a' will be depth 0, and 'b', 'c', 'd' will be depth 1.

Hope this helps.

叹梦 2024-12-15 15:47:12

你是对的。根应该包含在树遍历中。如果您尝试中序遍历,这一点尤其明显,因为(考虑到您的根有两个子节点)根应该位于两个子节点之间。谷歌一下,你会发现到处都是同样的行为。我什至检查了我的算法书。

我会确保我拥有该模块的最新版本,并将其报告为错误。

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.

荒岛晴空 2024-12-15 15:47:12

所以,我同意这里所有的海报,这可能有点令人困惑。然而,该模块已经很成熟,并且目前正在许多地方/代码库中使用,因此我不会接受像改变 \&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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文