为什么我需要明确引用 属性 才能修改它而不是使用引用?

Why am I required to explicitly refer to a property in order to modify it instead of using a reference?

我是 Perl 的新手。我第一次看到它是几天前,并决定练习实现一些算法和数据结构来学习。本例中为二叉搜索树 (BST)。

目前我正在使用一个 Node 包“Node.pm”,一个 BinarySearchTree 包“BST.pm”,以及一个使用代码“Main.pl”的入口点。

我的问题出现在使用对根 属性 的引用时插入树的根节点时(我知道它只是一个键 => 来自散列 table 的值,但是使用传统的 OOP术语我喜欢将其称为 属性)。

这是上下文的构造函数:

BST.pm

sub new() {

    my $type = shift;
    my $this = {};
    my $list = shift;

    $this->{'unordered_list'} = $list;
    $this->{'root'} = undef;

    bless($this, $type);
    return $this;
    
}

构造函数采用整数列表来构造二叉搜索树,并且根节点在第一次调用“插入”之前是未定义的。

在对“BuildUnbalancedTree”的调用中,我传递了对 'root' 属性:

的引用

Main.pl

$bst = BST->new($nums);

$bst->BuildUnbalancedTree($bst->{'root'});

$bst->TraverseInOrder($bst->{'root'});

BST.pm

sub BuildUnbalancedTree(){

    my $this = shift;
    my $root = shift;

    foreach my $key (@{$this->{'unordered_list'}}){

        my $newnode = $this->NewNode($key);
        $this->Insert($$root, $newnode);

    }

}

在“插入”方法中,如果我尝试使用传递给它的变量插入根节点,除了根节点之外,树被部分构建。子节点都设置好了,但是根节点本身的值永远不会设置。

这是我希望有效但无效的方法:

sub Insert(){

    my $this = shift;
    my $root = shift;
    my $node = shift;

    if(!$root) {
        
        $root = $node;

    } elsif($node->{'value'} > $root->{'value'}) {

        if(!$root->{'right'}) {
            $root->{'right'} = $node;
        } else {
            $this->Insert($root->{'right'}, $node);
        }

    } else {

        if(!$root->{'left'}){
            $root->{'left'} = $node;
        } else {
            $this->Insert($root->{'left'}, $node)
        }

    } 

}

假设第一个条件只会在第一次调用“插入”时为真,我更改了这一行:

$root = $node;

至:

$this->{'root'} = $node;

它确实有效,但我真的很想知道为什么另一种方法会出现问题。我相信这是因为我在对“BuildUnbalancedTree”的调用中取消引用了 root 参数(如果我不这样做,它会破坏一切)但真的很感谢确认和一些简短的专家分析。

您的方法 Insert 应该引用根而不是它的值。通过执行 $this->Insert($root, $newnode); 而不是 $this->Insert($$root, $newnode); 来调用它,并将 $root 的每个出现更改为 Insert 中的 $$root,并将递归调用替换为 $this->Insert($$root->{'right'}, $node);left 也是一样)。

问题是,当您执行 $this->Insert($$root, $newnode); 时,您将根的 传递给了 Insert。在第一次调用时,$root 尚未定义,这意味着您将 undef 传递给 Insert(它存储在其本地值 $root 中)。因此,执行 if (!$root) { $root = node; } 只会在本地设置 $root 的值。

请注意,一旦将对根的引用传递给 Insert(而不是值),就可以简化 Insert 方法:

sub Insert {
    my ($root, $node) = @_;

    if(!$$root) {
        $$root = $node;
    } elsif($node->{'value'} > $$root->{'value'}) {
        Insert($$root->{'right'}, $node);
    } else {
        Insert($$root->{'left'}, $node);
    }
}

我从参数中删除了 $this,因为您从未使用过它。用 Insert($root, $newnode);(而不是 $this->Insert(...))调用此 Insert


补充意见:

  • my $list = shift; 可能不是一个好主意:例如,如果你这样做:

    my @list = (12, 5, 6, 19);
    my $bst = BST->new(\@list);
    push @list, 42;
    $bst->BuildUnbalancedTree($bst->{'root'});
    

    您的 BST 现在包含 42,可能是 counter-intuitive。更好的方法可能是将列表(而不是数组引用)传递给构造函数:

    sub new {
        my $class   = shift;
        my @content = @_;
    
        my $this = { unordered_list => \@content };
    
        return bless($this, $class);
    }
    

    (请注意,我已经删除了 $this->{'root'} = undef;:当您尝试访问它时,Perl 的自动生成会自动创建它)。

  • 您的 BuildUnbalancedTree 方法将根作为参数,这有点令人惊讶,因为根存储在树中。我建议改为:

    sub BuildUnbalancedTree {
        my $this = shift;
    
        foreach my $key (@{$this->{'unordered_list'}}){
            my $newnode = $this->NewNode($key);
            $this->Insert($this->{root}, $newnode);
        }
    }