无法理解Perl哈希引用

Cha*_*ane -1 perl reference

我的Perl项目可以在多个系统上运行,但我不希望它们在不知道其他系统正在做什么的情况下单独运行.我需要保持他们的数据文件或多或少同步,这意味着合并他们的内容,所以我需要一个数据结构来包含每台计算机的详细信息以及对另一台计算机的引用.

正是这种引用导致了我的问题.

如果我将它简化为两台PC

use strict;
use warnings;

use Sys::Hostname;

# Peer computers
my $PC1 = {
    name => 'PC1',
    os   => 'Linux',
    data => '/its/data/directory',
    peer => 'PC2' #  But whas is really needed here is a reference to PC2's data structure below
};

my $PC2 = {
    name => 'PC2',
    os   => 'MSWin32',
    data => 'X:\\its\\data\\directory',
    peer => 'PC1' #  But whas is really needed here is a reference to PC1's data structure above
};

my $PEERS = [ $PC1, $PC2 ];

# Some code to set up the peer fields, for example
for my $p ( @$PEERS ) {

    my %Peer = %$p;

    if ( $Peer{name} eq 'PC1' ) {
        $Peer{peer} = $PC2;
    }
    else {
        $Peer{peer} = $PC1;
    }

    %$p = %Peer;    # I was surprised to find this line is necessary, otherwise the changes are lost
}

# Determine which system we are, and which we should pair with by default
for my $p ( @$PEERS ) {

    my %THIS = %$p;
    my %PEER = $THIS{peer};

    if ( ( $THIS{name} eq hostname ) && ( $THIS{os} eq $^O ) ) {
        print( $THIS{name}.', '.$PEER{name}."\n" );
        last;
    }
}
Run Code Online (Sandbox Code Playgroud)

这给出了以下内容

#perl test.pl
在test.pl第43行
找到偶数大小的列表的参考.参考文献找到了test.pl第43行的预期大小列表.

如何在对等字段中设置引用,以后可以解除引用?

mel*_*ene 6

如果你想要很好地介绍Perl中的引用,我推荐perldoc perlreftut.从您列出的语言中,C及其指针最接近Perl的引用(除了没有"引用算术"或无效引用).特别是,解除引用是Perl中的一个显式操作.

您正在尝试构造一个循环结构:$PC1包含对它的引用$PC2,并$PC2包含对它的引用$PC1.我建议不要这样做,因为Perl的自动内存管理基于引用计数.如果创建引用循环,则在手动中断循环之前不会自动释放它.(您可以通过明智地使用弱引用来解决这个问题,但是需要考虑哪些引用应该"拥有"结构,哪些引用不应该.)

实现这一点的最简单方法可能是保持数据结构不变,但将它们放在哈希中:

my %PEERS = (
    PC1 => {
        name    => 'PC1',
        os      => 'Linux',
        data    => '/its/data/directory',
        peer    => 'PC2',
    },
    PC2 => {
        name    => 'PC2',
        os      => 'MSWin32',
        data    => 'X:\\its\\data\\directory',
        peer    => 'PC1',
    },
);
Run Code Online (Sandbox Code Playgroud)

然后你可以编写如下代码:

for my $name (sort keys %PEERS) {
    my $THIS = $PEERS{$name};
    my $PEER = $PEERS{$THIS->{peer}};
    if ($THIS->{name} eq hostname && $THIS->{os} eq $^O) {
        print $THIS->{name} . ', ' . $PEER->{name} . "\n";
        last;
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,您现在有效地存储了两次名称(一次作为哈希键,一次作为(子)哈希中的字段),因此您可能更愿意将内容更改为:

my %PEERS = (
    PC1 => {
        os      => 'Linux',
        data    => '/its/data/directory',
        peer    => 'PC2',
    },
    PC2 => {
        os      => 'MSWin32',
        data    => 'X:\\its\\data\\directory',
        peer    => 'PC1',
    },
);

if (my $THIS = $PEERS{hostname()}) {
    if ($THIS->{os} eq $^O) {
        print "$THIS->{name}, $THIS->{peer}\n";
    }
}
Run Code Online (Sandbox Code Playgroud)

这节省了一个循环,因为我们可以通过主机名直接查找对等体.当然,这假设您的同伴名称是唯一的.


也就是说,如果你真的想要,你可以让你原来的尝试工作:

# my $PEERS = [ $PC1, $PC2 ];
# I'm not sure why you're using a reference to an array here.
# I'd just use an array, which saves us some dereferencing below:
my @PEERS = ($PC1, $PC2);

for my $Peer ( @PEERS ) {
    # my %Peer = %$p;
    # This line makes a copy of the %$p hash.
    # We want to modify %$p directly, so let's not do that.

    if ($Peer->{name} eq 'PC1') {
        $Peer->{peer} = $PC2;
    }
    else {
        $Peer->{peer} = $PC1;
    }
    # %$p = %Peer;    # I was surprised to find this line is necessary, otherwise the changes are lost
    # We don't need to copy the modified hash back because now we're modifying %$p directly.
}
Run Code Online (Sandbox Code Playgroud)

同样的,

for my $p ( @$PEERS ) {
    my %THIS = %$p;
    my %PEER = $THIS{peer};
Run Code Online (Sandbox Code Playgroud)

应该成为

for my $THIS ( @PEERS ) {
    my $PEER = $THIS->{peer};
Run Code Online (Sandbox Code Playgroud)

有没有必要复制%$p%THIS和你得到警告,因为您是分配$THIS{peer}(参考)到%PEER(散).如果你真的想在这里复制,你必须使用%PEER = %{ $THIS->{peer} }(取消引用引用,因为%{ $p }(%$p在原始代码中缩短为)).