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 映射,其中包含许多 设备。
备注:
- 我试图避免安装 CPAN 模块,因此脚本适用于类似的系统 (Proxmox VE)
- 最后一个设备(上面的deviceC)没有children
- 值 '' 没问题
- undef 可能会起作用
- 混合类型可以,但我需要知道如何设置
- 我永远不需要修改 或操作创建后的散列
- 我不知道递归 @array 以填充 %hash children 的正确方法。 * 我想要每个 USB 设备的水平数据
- 我会切换到 Object/package,但每个设备可以有一组不同的 children(或 none),因此无法知道 Object 名称
- 有些 USB 设备没有 children(根集线器)...类似于 %hash = ('deviceA' => '')
- 有些有 1 child 是最终设备...类似于 %hash = ('deviceA' => { 'deviceB' =>'' })
- 有些通过额外的集线器在根之间有多个步骤...类似于 %hash = ('deviceA' => { 'deviceB' => { 'deviceC' => '' } } ) 或更多
起点:
这是基本且不完整的,但会 运行:
#!/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 很陌生,我不打算在这里尝试解决这个问题。下面的想法是:
- 对于@array 中的每一项:
- 创建一种可以在下一次迭代中使用的方式(当前散列的 ref 或副本)来放置下一个 child
- 将项目附加为具有空值的前一次迭代的 child(如果有进一步的迭代,可以附加)
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)散列。然而,在这种情况下,您不需要初始化 %hash
:my %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
也许吧?
我一直在努力解决这个问题,并搜索了很多方法。我相信这会归结为非常基本的。
我在@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 映射,其中包含许多 设备。
备注:
- 我试图避免安装 CPAN 模块,因此脚本适用于类似的系统 (Proxmox VE)
- 最后一个设备(上面的deviceC)没有children
- 值 '' 没问题
- undef 可能会起作用
- 混合类型可以,但我需要知道如何设置
- 我永远不需要修改 或操作创建后的散列
- 我不知道递归 @array 以填充 %hash children 的正确方法。 * 我想要每个 USB 设备的水平数据
- 我会切换到 Object/package,但每个设备可以有一组不同的 children(或 none),因此无法知道 Object 名称
- 有些 USB 设备没有 children(根集线器)...类似于 %hash = ('deviceA' => '')
- 有些有 1 child 是最终设备...类似于 %hash = ('deviceA' => { 'deviceB' =>'' })
- 有些通过额外的集线器在根之间有多个步骤...类似于 %hash = ('deviceA' => { 'deviceB' => { 'deviceC' => '' } } ) 或更多
起点:
这是基本且不完整的,但会 运行:
#!/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 很陌生,我不打算在这里尝试解决这个问题。下面的想法是:
- 对于@array 中的每一项:
- 创建一种可以在下一次迭代中使用的方式(当前散列的 ref 或副本)来放置下一个 child
- 将项目附加为具有空值的前一次迭代的 child(如果有进一步的迭代,可以附加)
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)散列。然而,在这种情况下,您不需要初始化%hash
:my %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
也许吧?