3

我有一个 Perl 脚本,它解析数据文件并写入 5 个填充有 1100 x 1300 网格的输出文件。该脚本有效,但在我看来,它很笨拙并且可能效率不高。该脚本也是继承的代码,我对其进行了一些修改以使其更具可读性。尽管如此,还是一团糟。

目前,脚本读取数据文件(~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

5559是当前参数的数据线数。数据行:xy,该特定点的连续 x 值的数量,最后是values。参数之间有一个空行。

正如我之前所说,脚本有效,但我觉得这可以更容易和更有效地完成。我只是不知道怎么做。所以这是一个自我提升的机会。

有什么比数组和 for 循环的复杂组合更好的方法来解决这个问题?

编辑

应该更清楚这一点,对不起。

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

更新

我测试了解决方案,令我惊讶的是它比原始脚本慢(约 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();

 }
4

3 回答 3

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
于 2010-10-22T14:14:14.530 回答
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

希望这可以帮助。

于 2010-10-22T13:44:56.877 回答
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
于 2010-10-22T14:07:47.477 回答