在 Perl 中传递一片散列作为参数
Pass a slice of hash as argument in Perl
我的散列数据如下所示:
my %inputData;
$inputData{'312'} = 'foobar';
$inputData{'112'} = 'qwerty';
$inputData{'232'} = 'test123';
$inputData{'221'} = 'asdfg';
等等。
我用forks来分析数据,我用了$n个forks。 process() 函数启动一个新的 fork 来进行数据分析,如下所示:
for my $i ( 0 .. $n-1 )
{
process( ... );
}
如何将散列引用作为参数传递给包含一部分 %inputData 的 process() 函数?
例如,如果 $n = 2,循环将 运行 两次迭代,第一次迭代将执行:
my %hashSlice;
$hashSlice{'312'} = 'foobar';
$hashSlice{'112'} = 'qwerty';
process(\%hashSlice);
并在第二次迭代时执行:
my %hashSlice;
$hashSlice{'232'} = 'test123';
$hashSlice{'221'} = 'asdfg';
process(\%hashSlice);
或者,如果 $n = 3,循环将 运行 三次迭代,第一次迭代将执行:
my %hashSlice;
$hashSlice{'312'} = 'foobar';
$hashSlice{'112'} = 'qwerty';
process(\%hashSlice);
在第二次迭代时执行:
my %hashSlice;
$hashSlice{'232'} = 'test123';
process(\%hashSlice);
并在第三次迭代时执行:
my %hashSlice;
$hashSlice{'221'} = 'asdfg';
process(\%hashSlice);
你不能创建一个较小的散列,它是另一个子集的子集,而不像你写的那样以某种方式构建它
可能最好将整个散列连同要处理的键列表一起传递,如下所示
process( \%input_data, 'foobar', 'qwerty', 'test123')
您可以像这样使用切片来构建较小的散列
my @keys = ( 'foobar', 'qwerty', 'test123' );
my %subset;
@subset{@keys} = @input_data{@keys};
process(\%subset);
此外,您应该避免在词汇标识符中使用大写字母。大写保留用于全局标识符,例如 Package::Names,如果您也将它们用于局部变量和子例程
,可能会发生一些严重的冲突
我可以建议您不需要这样做吗?为什么不使用 Parallel::ForkManager
之类的东西,并为每个键生成一个新的分支——单独限制并发。
例如
#!/usr/bin/env perl
use strict;
use warnings;
use Parallel::ForkManager;
my $fm = Parallel::ForkManager -> new ( 3 );
foreach my $key ( keys %inputData ) {
$fm -> start and next;
process ( $inputData{$key} );
$fm -> finish;
}
$fm -> wait_all_children();
这会将您的并发限制设置为 3,但会为每个元素生成一个新的分支,并让您只需更改并发数即可轻松扩展 'wider'。
否则我会考虑也许切换到使用 threads
并通过 Thread::Queue
将元素馈送到多个工作线程。
如果这样做的目的是在工作人员之间分配工作,那么从公共队列中获取工作的工作人员池模型会更好。 Parallel::Manager 解决方案 Sobrique 就是一个例子(尽管重用工人可能更好)。
一个简单的解决方案:
my %data = ...;
my $num_groups = ...;
my @groups;
my $i = 0;
for my $key (keys(%data)) {
$groups[$i]{$key} = $data{$key};
$i = ($i + 1) % $num_groups;
}
可能会快一点,尤其是对于大输入。
my %data = ...;
my $num_groups = ...;
our @keys; local *keys = sub { \@_ }->( keys(%data) );
my $r = @keys % $num_groups;
my $group_size = ( @keys - $r ) / $num_groups;
for my $i (0..$num_groups-1) {
our @group_keys; local *group_keys = sub { \@_ }->(
splice(@keys, 0, $group_size + ( $i < $r ? 1 : 0 ))
);
my %group;
@group{@group_keys} = @data{@group_keys};
push @groups, \%group;
}
备注:
our @a; local *a = sub { \@_ }->( LIST );
类似于
my @a = LIST;
除了 @a
的元素是 LIST
返回的实际标量,而不是它们的副本。
从 5.20 开始,
my %group;
@group{@group_keys} = @data{@group_keys};
push @groups, \%group;
可以写
push @groups, { %data{@group_keys} };
我的散列数据如下所示:
my %inputData;
$inputData{'312'} = 'foobar';
$inputData{'112'} = 'qwerty';
$inputData{'232'} = 'test123';
$inputData{'221'} = 'asdfg';
等等。
我用forks来分析数据,我用了$n个forks。 process() 函数启动一个新的 fork 来进行数据分析,如下所示:
for my $i ( 0 .. $n-1 )
{
process( ... );
}
如何将散列引用作为参数传递给包含一部分 %inputData 的 process() 函数?
例如,如果 $n = 2,循环将 运行 两次迭代,第一次迭代将执行:
my %hashSlice;
$hashSlice{'312'} = 'foobar';
$hashSlice{'112'} = 'qwerty';
process(\%hashSlice);
并在第二次迭代时执行:
my %hashSlice;
$hashSlice{'232'} = 'test123';
$hashSlice{'221'} = 'asdfg';
process(\%hashSlice);
或者,如果 $n = 3,循环将 运行 三次迭代,第一次迭代将执行:
my %hashSlice;
$hashSlice{'312'} = 'foobar';
$hashSlice{'112'} = 'qwerty';
process(\%hashSlice);
在第二次迭代时执行:
my %hashSlice;
$hashSlice{'232'} = 'test123';
process(\%hashSlice);
并在第三次迭代时执行:
my %hashSlice;
$hashSlice{'221'} = 'asdfg';
process(\%hashSlice);
你不能创建一个较小的散列,它是另一个子集的子集,而不像你写的那样以某种方式构建它
可能最好将整个散列连同要处理的键列表一起传递,如下所示
process( \%input_data, 'foobar', 'qwerty', 'test123')
您可以像这样使用切片来构建较小的散列
my @keys = ( 'foobar', 'qwerty', 'test123' );
my %subset;
@subset{@keys} = @input_data{@keys};
process(\%subset);
此外,您应该避免在词汇标识符中使用大写字母。大写保留用于全局标识符,例如 Package::Names,如果您也将它们用于局部变量和子例程
,可能会发生一些严重的冲突我可以建议您不需要这样做吗?为什么不使用 Parallel::ForkManager
之类的东西,并为每个键生成一个新的分支——单独限制并发。
例如
#!/usr/bin/env perl
use strict;
use warnings;
use Parallel::ForkManager;
my $fm = Parallel::ForkManager -> new ( 3 );
foreach my $key ( keys %inputData ) {
$fm -> start and next;
process ( $inputData{$key} );
$fm -> finish;
}
$fm -> wait_all_children();
这会将您的并发限制设置为 3,但会为每个元素生成一个新的分支,并让您只需更改并发数即可轻松扩展 'wider'。
否则我会考虑也许切换到使用 threads
并通过 Thread::Queue
将元素馈送到多个工作线程。
如果这样做的目的是在工作人员之间分配工作,那么从公共队列中获取工作的工作人员池模型会更好。 Parallel::Manager 解决方案 Sobrique 就是一个例子(尽管重用工人可能更好)。
一个简单的解决方案:
my %data = ...;
my $num_groups = ...;
my @groups;
my $i = 0;
for my $key (keys(%data)) {
$groups[$i]{$key} = $data{$key};
$i = ($i + 1) % $num_groups;
}
可能会快一点,尤其是对于大输入。
my %data = ...;
my $num_groups = ...;
our @keys; local *keys = sub { \@_ }->( keys(%data) );
my $r = @keys % $num_groups;
my $group_size = ( @keys - $r ) / $num_groups;
for my $i (0..$num_groups-1) {
our @group_keys; local *group_keys = sub { \@_ }->(
splice(@keys, 0, $group_size + ( $i < $r ? 1 : 0 ))
);
my %group;
@group{@group_keys} = @data{@group_keys};
push @groups, \%group;
}
备注:
our @a; local *a = sub { \@_ }->( LIST );
类似于
my @a = LIST;
除了
@a
的元素是LIST
返回的实际标量,而不是它们的副本。从 5.20 开始,
my %group; @group{@group_keys} = @data{@group_keys}; push @groups, \%group;
可以写
push @groups, { %data{@group_keys} };