2010-10-22 13 views
3

我有一个Perl脚本,它解析数据文件并写入5个输出文件,填充1100 x 1300网格。剧本的作品,但在我看来,这是笨拙的,可能是无效的。该脚本也是继承代码,我修改了一些代码以使其更具可读性。尽管如此,这是一团糟。如何用Perl高效填充N×M网格?

目前,该脚本读取数据文件(〜4MB),并将其放入数组。然后循环遍历数组解析其内容并将值推送到另一个数组,最后将其打印到另一个for循环中。如果某个点没有找到值,则打印9999.零点是可接受的值。

数据文件有5个不同的参数和他们每个人被写入其自身的文件。数据的

实施例:

data for the param: 2 
5559 
// (x,y) count values 
280 40 3 0 0 0 
280 41 4 0 0 0 0 
280 42 5 0 0 0 0 0 
281 43 4 0 0 10 10 
281 44 4 0 0 10 10 
281 45 4 0 0 0 10 
281 46 4 0 0 10 0 
281 47 4 0 0 10 0 
281 48 3 10 10 0 
281 49 2 0 0 
41 50 3 0 0 0 
45 50 3 0 0 0 
280 50 2 0 0 
40 51 8 0 0 0 0 0 0 0 0 
... 

data for the param: 3 
3356 
// (x,y) count values 

是数据线到当前参数的数目。数据线变为:Xÿ数量的连续的x值该特定点和最后的。 参数之间有空行。

正如我刚才所说,剧本的作品,但我觉得这可能是这样做更容易,更有效。我只是不知道如何。所以这是一个自我提升的机会。

会是什么这个问题更好的办法,比阵列和for循环一个复杂的组合?

编辑

应该更清楚这一点,不好意思。

输出是1100 X 1300网格填充有从数据文件中读取的值。每个参数写入不同的文件。数据行上的多个值意味着该行具有x(+ n),y个点的数据。

UPDATE

我测试的解决方案,让我吃惊的是比原来的脚本(〜3秒)慢。但是,该脚本的体积缩小了大约50%,这使得更容易理解脚本的功能。在这种情况下,这比速度增加3秒更重要。

这里有一些旧脚本的代码。希望你能从中得到基本的想法。为什么它更快?

for my $i (0..$#indata) { # Data file is read to @indata 
... 
    if($indata[$i] =~ /^data for the param:/) { 
    push @block, $i; # data borders aka. lines, where block starts and ends 
    } 
... 
} 
    # Then handle the data blocks 
for my $k (0..4) { # 5 parameters 
... 
    if($k eq '4') { # Last parameter 
    $enddata = $#indata; 
    } 
    else { 
    $enddata = $block[$k+1]; 
    } 
    ... 
    for my $p ($block[$k]..$enddata) { # from current block to next block 
    ... 
    # Fill data array 
    for(my $m=0 ; $m<$n ; $m++){ 
    $data[$x][$y] = $values[$m]; 
    } 

    } 
    print2file(); 

} 
+0

是否有可能在此发布部分代码?至少执行你描述的处理的部分? – Sagar 2010-10-22 13:42:53

+2

此外,您不会在输出文件中描述您想要的内容!在这种情况下,每个坐标的多个值的含义是什么?描述你的输入,但不是你想要达到的目标对于一个问题来说毫无用处。 – tsee 2010-10-22 13:54:05

回答

1

以下将填写一个散列中的稀疏数组。打印时,为未定义值的单元打印9999。我更改了代码以将每行构建为一个字符串以减少内存占用。

#!/usr/bin/perl 

use strict; use warnings; 
use YAML; 

use constant GRID_X => 1100 - 1; 
use constant GRID_Y => 1300 - 1; 

while (my $data = <DATA>) { 
    if ($data =~ /^data for the param: (\d)/) { 
     process_param($1, \*DATA); 
    } 
} 

sub process_param { 
    my ($param, $fh) = @_; 
    my $lines_to_read = <$fh>; 
    my $lines_read = 0; 

    $lines_to_read += 0; 

    my %data; 

    while (my $data = <$fh>) { 
     next if $data =~ m{^//}; 
     last unless $data =~ /\S/; 
     $lines_read += 1; 

     my ($x, $y, $n, @vals) = split ' ', $data; 

     for my $i (0 .. ($n - 1)) { 
      $data{$x + $i}{$y} = 0 + $vals[$i]; 
     } 
    } 
    if ($lines_read != $lines_to_read) { 
     warn "read $lines_read lines, expected $lines_to_read\n"; 
    } 

    # this is where you would open a $param specific output file 
    # and write out the full matrix, instead of printing to STDOUT 
    # as I have done. As an improvement, you should probably factor 
    # this out to another sub. 

    for my $x (0 .. GRID_X) { 
     my $row; 
     for my $y (0 .. GRID_Y) { 
      my $v = 9999; 
      if (exists($data{$x}) 
        and exists($data{$x}{$y}) 
        and defined($data{$x}{$y})) { 
       $v = $data{$x}{$y}; 
      } 
      $row .= "$v\t"; 
     } 
     $row =~ s/\t\z/\n/; 
     print $row; 
    } 

    return; 
} 


__DATA__ 
data for the param: 2 
5559 
// (x,y) count values 
280 40 3 0 0 0 
280 41 4 0 0 0 0 
280 42 5 0 0 0 0 0 
281 43 4 0 0 10 10 
281 44 4 0 0 10 10 
281 45 4 0 0 0 10 
281 46 4 0 0 10 0 
281 47 4 0 0 10 0 
281 48 3 10 10 0 
281 49 2 0 0 
41 50 3 0 0 0 
45 50 3 0 0 0 
280 50 2 0 0 
40 51 8 0 0 0 0 0 0 0 0 
+1

谢谢。这是很好和简单的解决方案。当前脚本通过数组循环查找每个参数的数据边界。根据这些边界为for循环中的每个参数填充网格。这是很多循环!写入文件与解决方案中的文件几乎相同。 – Veivi 2010-10-24 14:11:23

0

如果您使用引用,Perl支持多维数组。

my $matrix = []; 
$matrix->[0]->[0] = $valueAt0x0; 

所以你可以读了整个事情的一个去

$matrix = []; 
while($ln = <INPUT>) { 
    @row = split(/ /, @ln); #assuming input is separated by spaces 
    push(@$matrix, \@row); 
} 
# here you read matrix. Let's print it 
foreach my $row (@$matrix) { 
    print join(",", @{$row}) . "\n"; 
} 
# now you pruinted your matrix with "," as a separator 

希望这有助于。

0

既然你不与您期望的输出,这是不可能知道该怎么写文件。但是这个阅读部分以非常灵活的方式。您可能可能会微调优化正则表达式的数量,或者失去使用隐式主题变量$_以提高易读性。如果你愿意在调用flush_output之前为矩阵的每个单元格输入一个特定的输出格式(例如“所有用逗号连接的值”),那么你可以去掉最内层的数组,然后执行$matrix[$x][$y] .= ($matrix[$x][$y] ? ',' : '') . join(',', @data);或类似的操作并且不那么模糊。

use strict; 
use warnings; 

my $cur_param; 
my @matrix; 
while (<DATA>) { 
    chomp; 
    s/\/\/.*$//; 
    next if /^\s*$/; 

    if (/^data for the param: (\d+)/) { 
    flush_output($cur_param, \@matrix) if defined $cur_param; 
    $cur_param = $1; 
    @matrix =(); # reset 
    # skip the line with number of rows, we're smarter than that 
    my $tmp = <DATA>; 
    next; 
    } 

    (my $x, my $y, undef, my @data) = split /\s+/, $_; 
    $matrix[$x][$y] ||= []; 
    push @{$matrix[$x][$y]}, @data; 
} 

sub flush_output { 
    my $cur_param = shift; 
    my $matrix = shift; 
    # in reality: open file and dump 
    # ... while dumping, do an ||= [9999] for the default... 

    # here: simple debug output: 
    use Data::Dumper; 
    print "\nPARAM $cur_param\n"; 
    print Dumper $matrix; 
} 

__DATA__ 
data for the param: 2 
5559 
// (x,y) count values 
280 40 3 0 0 0 
280 41 4 0 0 0 0 
280 42 5 0 0 0 0 0 
281 43 4 0 0 10 10 
281 44 4 0 0 10 10 
281 45 4 0 0 0 10 
281 46 4 0 0 10 0 
281 47 4 0 0 10 0 
281 48 3 10 10 0 
281 49 2 0 0 
41 50 3 0 0 0 
45 50 3 0 0 0 
280 50 2 0 0 
40 51 8 0 0 0 0 0 0 0 0 

data for the param: 3 
3356 
// (x,y) count values