我需要根据作业ID将一个文件中的数据拆分为其他文件。文件的结构和两个帮助文件是:
# data.txt - data file - node,timestamp,data
1,1516,25
2,1845,24
3,1637,26
4,1342,74
5,1426,63
6,1436,23
7,1732,64
1,1836,83
2,1277,12
3,2435,62
4,2433,47
5,2496,52
6,2142,69
7,2176,53
# job.txt - job timing - job,startts,endts
1234,1001,2000
5678,2001,2500
# node.txt - node to job map - job,node
1234,1
1234,2
1234,3
1234,4
1234,5
5678,3
5678,4
5678,5
5678,6
5678,7
为了将数据文件中的一行映射到适当的新文件,必须进行两次转换。首先,必须使用数据时间戳来确定正在运行的作业。其次,必须检查正在运行的作业列表,以确定哪个拥有数据引用的节点。这是我的解决方案:
use strict;
use warnings;
my @timing = ( );
my %nodes = ( );
my %handles = ( );
### array of arrays containing job, start time, and end time
open JOB, "<job.txt" or die "can't open jobfile, $!";
while (<JOB>) {
my @fields = split /,/; #/ stop SO highliter
my @array = ($fields[0], $fields[1], $fields[2]);
push @timing, \@array;
}
close JOB;
### map job -> array of nodes
open NID "<node.txt" or die "can't open nidfile";
while (<NID>) {
my @fields = split /,/; #/
if (!exists $nodes{$fields[0]}) { $nodes{$fields[0]} = (); }
push @{$nodes{$fields[0]}}, $fields[1];
}
close NID;
### split data
open DATA, "<data.txt" or die "Couldn't open file all.pow, $!";
while (<DATA>) {
my @fields = split /,/; #/
my @jobs = grep {$fields[1] >= $_->[1] && $fields[1] <= $_->[2]} @timing;
scalar @jobs > 0 or next;
my $jid = (grep {!exists $nodes{$fields[0]}} @jobs)[0][0];
### create and memoize file handles
if (!exists $handles{$jid}) {
open my $temp, ">$jid.txt" or die "Can't open jfile $jid, $!";
$handles{$jid} = $temp;
}
print {$handles{$jid}} "$fields[1],fields[2]";
}
close DATA;
我想知道是否有任何方法可以提高数据文件循环的速度/效率。这必须在大量数据上运行,因此需要尽可能高效。我还要感谢有关更多惯用方法的任何评论:这是我的第一个perl脚本(对数组的引用数组花了很长时间才弄明白)。
答案 0 :(得分:2)
至于效率,如果此代码经常写入文件(并且需要动态打开许多文件),我就不会看到一种显着加快速度的方法。允许预处理或后处理可能有所帮助,但这取决于更广泛的背景。
以下是一些编码点的快速搜索。按顺序出现
完全没有使用&#34;初始化&#34;数组和哈希到空列表;只是宣布他们
总是use lexical filehandles和三个参数open
(就像你写作一样)。所以
open my $job_fh, '<', ...
无需先创建密钥即可分配给它。下面的第一行是不需要的
#if (!exists $nodes{$fields[0]}) { $nodes{$fields[0]} = (); } # NOT needed
push @{$nodes{$fields[0]}}, $fields[1];
这是由autovivification提供的:如果一个对象在左值上下文中被取消引用(当它可以修改时),如果它是未定义的,则会创建它。这是一个复杂的功能。接近您的使用是this post;另请参见此EffectivePerler article和this post及其链接。
使用数组或列表slices。而不是
my @array = ($fields[0], $fields[1], $fields[2]);
一个人可以做到
my @array = @fields[0,1,2]; # or @fields[0..2] in this case
然后也可以做
push @timing, [ @fields[0..2] ];
或
push @timing, [ (split /,/)[0..2] ]; # no need for @fields now
由于没有创建中间数组,所以效率更高。但是后两者的灵活性较差,限制了中间处理或检查的可能性。如果这就是你所做的一切
my @timing = map { [ (split /,/)[0..2] ] } <$job_fh>;
也是如此,但它更不灵活(并且一次读取所有行†)
计算标量context中的数组以返回元素数。所以
scalar @jobs > 0 or next;
不需要scalar
,您可以写
@jobs > 0 or next;
减少到
@jobs or next;
但我宁愿写为
next if not @jobs; # or: next unless @jobs;
使用statement modifier(&#34;后缀语法&#34;)
† <>
operator位于map
强加的列表上下文(见上面的链接)中,因此它返回所有行