Perl ...使用@array 项创建水平 children 的 %hash

Perl ... create horizontal children of a %hash using @array items

我一直在努力解决这个问题,并搜索了很多方法。我相信这会归结为非常基本的。

我在@array 中有数据,我想将其移动到 %hash 中的树中。

这可能更适合 JSON?但是我之前没有深入研究过,我不需要保存out/restore这些信息。

愿望:

创建可以相互嵌套的 USB 设备的依赖树,可以通过集线器 (deviceB) 跟踪端点 (deviceC),最后跟踪根 (deviceA)。

示例:

简化版(我希望...这不是来自实际的更长脚本):

我想转换成这种格式的数组:

my @array = ['deviceA','deviceB','deviceC'];

到多维哈希等于:

my %hash = ('deviceA' => { 'deviceB' => { 'deviceC' => '' } } )

会像这样转储:

$VAR1 = {
          'deviceA' => {
                         'deviceB' => {
                                        'deviceC' => ''
                                      }
                       }
        };

对于只查看单个设备,这不是必需的,但我正在构建一个 IOMMU -> PCI 设备 -> USB 映射,其中包含许多 设备。

备注:

起点:

这是基本且不完整的,但会 运行:

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper qw(Dumper);

# data in from parsing usb device path:
my @array = ['deviceA','deviceB','deviceC'];
# needs to be converted to:
my %hash = ('deviceA' => { 'deviceB' => { 'deviceC' => '' } } );

print "\n\%hash:\n" . Dumper \%hash;

Pseudo-code

此部分不是任何形式的工作代码。我只是想记下我的想法。我知道格式不对,我已经尝试了多种方法来创建这个并且 我看起来更笨 显示我所有的尝试:)

我对 refs 很陌生,我不打算在这里尝试解决这个问题。下面的想法是:

my @array = ['deviceA','deviceB','deviceC'];
my %hash = {};
my %trackref;
for (@array) {
  %trackref = %hash; # a copy of the existing that won't change when %hash updates
  $hash{last_child} ::append_child:: $_;
}

您实际上已经很接近了,但是您似乎需要更好地理解引用。 perldoc perlref 可能是理解引用的一个很好的起点。

在查看解决方案之前,您的代码中存在一些错误:

  • my @array = [ ... ];: [] 创建一个 arrayref,而不是数组,这意味着 @array 实际上存储单个标量项:对另一个数组的引用。使用()初始化一个数组:my @array = ( ... );.

  • my %hash = {};:类似地,{} 创建一个哈希引用,而不是哈希。这意味着此行在 %hash 中存储了一个 hashref,这将导致此警告:Reference found where even-sized list expected at hash.pl line (因为散列包含 keys-values 而您只提供了一个密钥)。使用 () 作为简单的(即不是 hashref)散列。然而,在这种情况下,您不需要初始化 %hashmy %hash;my %hash = () 做同样的事情(即创建一个空哈希)。

  • %trackref = %hash;%hash内容复制到%trackref中。这意味着,与名称“trackref”所暗示的相反,%trackref 不包含对任何内容的引用,而是 %hash 的副本。使用 \%hash 创建对 %hash.
    的引用 请注意,如果您已经有一个 hashref,则将其分配给另一个变量会复制该引用。例如,如果您执行 my $hash1 = {}; my $hash2 = $hash1,那么 $hash1$hash2 都引用相同的散列。

因此,在您尝试解决这些问题后,我们得到:

my @array = ('deviceA','deviceB','deviceC');
my %hash;
my $trackref = \%hash;
for my $usb (@array) {
    $trackref->{$usb} = {};
    $trackref = $trackref->{$usb};
}

print Dumper \%hash;

输出:

$VAR1 = {
          'deviceA' => {
                         'deviceB' => {
                                        'deviceC' => {}
                                      }
                       }
        };

我所做的主要更改是将您的 $hash{last_child} ::append_child:: $_; 替换为 $trackref->{$_} = {};。但想法保持不变:将项目附加为具有空值的上一次迭代的子项 以重复使用您的话。

为了帮助您更好地理解代码,让我们一步步看看循环中发生了什么:

  • 在第一次迭代之前,%hash 为空,$trackref 引用 %hash.

  • 在第一次迭代中,我们将 deviceA => {} 放在 $trackref 中(或者更迂腐地说,我们将 {} 与键 deviceA 关联起来$trackref)。由于 $trackref 引用了 %hash,这会将 deviceA => {} 放入 %hash。然后,我们将刚刚创建的新 {} 存储在 $trackref 中,这意味着 $trackref 现在引用 $hash{deviceA}.

  • 在第二次迭代中,我们将deviceB => {}放入$trackref$trackeref 引用 $hash{deviceA}(我们在上一次迭代中创建),这意味着 %hash 现在是 (deviceA => { deviceB => {} })。然后我们将新的 {}.

    存储在 $trackref
  • 等等...

您会注意到,在最里面的散列中,{} 与键 deviceC 相关联。当迭代散列时,你可以通过做类似 if (%$hash) 的事情来知道你是否在最后(而不是 if ($hash) 如果最后一个 {} 本来是 undef'')。让我知道这是否是一个问题:我们可以添加一些代码将此 {} 转换为 undef(或者,您可以自己做,这将是一个很好的练习来习惯引用)


小注:@array%hash 是糟糕的数组和散列名称,因为 @ 已经表示数组,而 % 已经表示散列。您可能只是将这些名称用于您的问题的这个小示例,在这种情况下,没问题。但是,如果您在实际代码中使用这些名称,请考虑将它们更改为更明确的内容... @usb_devices%usb_devices_tree 也许吧?