2010-10-22 5 views
3

J'ai un script Perl, qui analyse le fichier de données et écrit 5 fichiers de sortie remplis de grille 1100 x 1300. Le script fonctionne, mais à mon avis, c'est maladroit et probablement inefficace. Le script est aussi du code hérité, que j'ai modifié un peu pour le rendre plus lisible. Pourtant, c'est un gâchis. À l'heure actuelle, le script lit le fichier de données (~ 4 Mo) et le place dans un tableau. Ensuite, il parcourt le tableau en analysant son contenu et en poussant les valeurs vers un autre tableau, puis en les imprimant dans un autre fichier. Si la valeur n'est pas trouvée pour un certain point, elle imprime 9999. Le zéro est une valeur acceptable.Comment remplir une grille N x M efficacement avec Perl?

Le fichier de données a 5 paramètres différents et chacun d'eux est écrit dans son propre fichier.

Exemple de données:

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 

est le nombre de lignes de données pour le paramètre en cours. Ligne de données va: x, y, nombre de valeurs x consécutives pour ce point particulier et enfin les valeurs . Il y a une ligne vide entre les paramètres. Comme je l'ai dit plus tôt, le script fonctionne, mais je pense que cela pourrait être fait beaucoup plus facilement et plus efficacement. Je ne sais pas comment. Alors, voici une chance pour l'auto-amélioration.

Quelle serait une meilleure approche de ce problème, qu'une combinaison compliquée de tableaux et de boucles?

EDIT:

aurait dû être plus clair à ce sujet, désolé.

La sortie est une grille de 1100 x 1300 remplie de valeurs lues dans le fichier de données. Chaque paramètre est écrit dans un fichier différent. Plus d'une valeur sur la ligne de données signifie que cette ligne contient des données pour x (+ n), y points.

MISE À JOUR:

J'ai testé la solution et à ma grande surprise, il était plus lent que script original (~ 3 secondes). Cependant, le script est environ 50% plus petit, ce qui facilite beaucoup la compréhension du script. Dans ce cas, c'est plus important qu'un gain de vitesse de 3 secondes.

Voici une partie du code de l'ancien script. J'espère que vous aurez l'idée de base. Pourquoi est-ce plus rapide?

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

Serait-il possible de publier une partie de votre code ici? La partie qui fait au moins le traitement que vous décrivez? – Sagar

+2

En outre, vous ne décrivez pas exactement ce que vous voulez dans les fichiers de sortie! Quelle est la signification des valeurs multiples par coordonnée dans ce contexte? Décrire votre contribution, mais pas ce que vous voulez accomplir est assez inutile pour une question. – tsee

Répondre

1

Ce qui suit remplira un tableau fragmenté dans un hachage. Lors de l'impression, imprimez 9999 pour les cellules avec des valeurs indéfinies. J'ai changé le code pour construire chaque ligne comme une chaîne pour réduire l'empreinte de la mémoire.

#!/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

Merci. C'est une solution agréable et simple. Présentez les boucles de script via un tableau en recherchant les limites de données pour chaque paramètre. La grille est remplie pour chaque paramètre dans la boucle for en fonction de ces limites. C'est beaucoup de boucles! L'écriture dans un fichier est presque la même que dans votre solution. – Veivi

0

Perl prend en charge les tableaux multidimensionnels si vous utilisez des références.

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

Ainsi, vous pouvez le lire en entier en une seule fois

$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 

Hope this helps.

0

Étant donné que vous ne définissez pas la sortie souhaitée, il est impossible de savoir quoi écrire dans les fichiers. Mais ceci fait la partie de lecture d'une manière assez flexible.Vous pourriez probablement micro-optimiser le nombre d'expressions régulières ou perdre l'utilisation de la variable de sujet implicite $_ pour une meilleure lisibilité. Si vous êtes prêt à valider un certain format de sortie pour chaque cellule de la matrice avant d'appeler flush_output (comme "toutes les valeurs jointes par des virgules"), vous pouvez supprimer la couche la plus interne des tableaux et faire $matrix[$x][$y] .= ($matrix[$x][$y] ? ',' : '') . join(',', @data); et moins obscur.

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 
Questions connexes