2010-10-12 13 views
2

J'ai un script Perl pour analyser plusieurs mégaoctets de données ligne par ligne. Par exemple, j'utiliserai la clôture du Dow Jones Average par jour sur plusieurs lignes.Perl: Template pour un tableau anonyme?

Les données sont lues et la mise en page des données dans le fichier est simple. Dans cet exemple, il est:

Date Open High Low Close Volume Adj-Close

Comme je l'ai lu les données, de nombreux calculs sont effectués. Certaines des données et les calculs sont conservés pour une utilisation ultérieure. Disons que les nouvelles données sont:

Date Open Adj-Close %change [more data to be added]

Voici quelques exemples de code:

use warnings; use strict; 

my @trades; 

while(<DATA>) { 
    chomp; 
my ($year,$mon,$day,$open,$high,$low,$close,$vol,$ad_close); 

if(($year,$mon,$day,$open,$high,$low,$close,$vol,$ad_close)= 
    /^(\d+)-(\d+)-(\d+), #date YYYY-MM-DD 
    (\d+\.\d+),    #open 
    (\d+\.\d+),    #High 
    (\d+\.\d+),    #Low 
    (\d+\.\d+), #Close 
    (\d+),  #Vol 
    (\d+\.\d+)/x) #adj cl 
    { 
    my $drp=($ad_close-$open)/$open; 
# HERE Created:  
    push @trades, [$year,$mon,$day,$open,$ad_close,$drp];  
    } 
    else { 
     print "$_ does not match...\n"; 
    } 
} 

# widely separated and in multiple places... 

foreach my $trade_ref (@trades) { 
#HERE Referenced 
my ($year,$mon,$day,$open,$ad_close,$drp)[email protected]$trade_ref; 

print "$year-$mon-$day $open,$ad_close,$drp\n";   
} 

# Dow Jones data by day... 
__DATA__ 
2010-10-08,10948.50,11055.29,10901.12,11006.48,3871420000,11006.48 
2010-10-07,10968.41,11032.17,10878.04,10948.58,3910550000,10948.58 
2010-10-06,10936.79,11015.86,10880.08,10967.65,4073160000,10967.65 
2010-10-05,10752.63,10982.98,10752.63,10944.72,4068840000,10944.72 
2010-10-04,10828.85,10875.54,10682.66,10751.27,3604110000,10751.27 
2010-10-01,10789.72,10907.41,10759.14,10829.68,4298910000,10829.68 
2010-09-30,10835.96,10960.99,10732.27,10788.05,4284160000,10788.05 
2010-09-29,10857.98,10901.96,10759.75,10835.28,3990280000,10835.28 
2010-09-28,10809.85,10905.44,10714.03,10858.14,4025840000,10858.14 
2010-09-27,10860.03,10902.52,10776.44,10812.04,3587860000,10812.04 
2010-09-24,10664.39,10897.83,10664.39,10860.26,4123950000,10860.26 
2010-09-23,10738.48,10779.65,10610.12,10662.42,3847850000,10662.42 
2010-09-22,10761.11,10829.75,10682.40,10739.31,3911070000,10739.31 
2010-09-21,10753.39,10844.89,10674.83,10761.03,4175660000,10761.03 
2010-09-20,10608.08,10783.51,10594.38,10753.62,3364080000,10753.62 
2010-09-17,10595.44,10689.29,10529.67,10607.85,4086140000,10607.85 
2010-09-16,10571.75,10624.58,10499.43,10594.83,3364080000,10594.83 
2010-09-15,10526.42,10609.21,10453.15,10572.73,3369840000,10572.73 
2010-09-14,10544.81,10622.69,10460.34,10526.49,4521050000,10526.49 
2010-09-13,10458.60,10605.73,10458.45,10544.13,4521050000,10544.13 
2010-09-10,10415.01,10502.80,10376.34,10462.77,3061160000,10462.77 
2010-09-09,10388.22,10515.86,10359.23,10415.24,3387770000,10415.24 
2010-09-08,10338.57,10460.50,10318.93,10387.01,3224640000,10387.01 
2010-09-07,10446.80,10448.99,10304.44,10340.69,3107380000,10340.69 
2010-09-03,10321.92,10484.71,10321.92,10447.93,3534500000,10447.93 
2010-09-02,10270.08,10350.98,10211.80,10320.10,3704210000,10320.10 
2010-09-01,10016.01,10305.87,10016.01,10269.47,4396880000,10269.47 
2010-08-31,10006.42,10101.53,9915.73,10014.72,4038770000,10014.72 

Au lieu dans le code marqué #Ici, notez que je suis pousser un tableau anonyme sur un autre tableau nommé pour un accès ultérieur. Plus tard (beaucoup plus tard dans le vrai programme) j'accède au même tableau par référence.

Jusqu'à présent, je viens de couper le texte du "modèle" [$year,$mon,$day,$open,$ad_close,$drp] du premier # ICI et coller manuellement my ($year,$mon,$day,$open,$ad_close,$drp)[email protected]$trade_ref; dans les autres # ICI dans le programme. Doit être un meilleur moyen ...

Y a-t-il un moyen pour que je puisse avoir un modèle du tableau anonyme que j'appuie afin qu'il soit automatiquement référencé de manière ordonnée dans les autres cas à travers le script? Dans le script réel, les résultats analysés tels que $drp changent et je veux un changement dans la création des données à gérer gracieusement par les routines ultérieures même si je ne change pas my ($year,$mon,$day,$open,$ad_close,$drp)[email protected]$trade_ref; S'il s'agissait d'un programme C, la définition pourrait être un macro dans un seul endroit ...

Je songe à pousser un hachage anonyme au lieu d'un tableau avec le hachage ayant des paires nom/valeur qui ont le nom de la valeur puis la valeur de cette valeur. Théoriquement, je sais que cela fonctionnerait, mais cela me semble inutile et lent. Il y a environ 2 Go de données dans le jeu de données réel, et je peux le faire rapidement avec la conception actuelle.

Y a-t-il un meilleur moyen?

+0

Vous pouvez enregistrer une ligne de code dupliqués en faisant la déclaration des variables et l'affectation ensemble: 'si (ma (année $, ...) = ...)' – daxim

Répondre

3
use strict; use warnings; 
use constant { 
    YEAR => 0, MON  => 1, DAY => 2, 
    OPEN => 3, AD_CLOSE => 4, DRP => 5, 
}; 

my @trades; 
while(<DATA>) { 
    chomp; 
    my $dow = parse_dow($_); 
    push @trades, $dow if @$dow; 
} 

print "@{$_}[YEAR, MON, DAY, OPEN, AD_CLOSE, DRP]\n" 
for @trades; 

sub parse_dow { 
    my ($dow) = @_; 

    my ($date, $open, $high, $low, $close, $vol, $ad_close) 
    = split /,/, $dow;   
    my ($year, $mon, $day) = split /-/, $date; 
    my $drp = ($ad_close - $open)/$open; 

    return [$year, $mon, $day, $open, $ad_close, $drp]; 
} 

__DATA__ 
2010-10-08,10948.50,11055.29,10901.12,11006.48,3871420000,11006.48 
2010-10-07,10968.41,11032.17,10878.04,10948.58,3910550000,10948.58 
2010-10-06,10936.79,11015.86,10880.08,10967.65,4073160000,10967.65 
+0

Je pense que c'est un bon début, et moi a penser. Avec la 'use constant', je pourrais modifier le retour pour avoir le même comportement et réaliser ce que je décris je pense ... +1, merci! – dawg

+0

Je voudrais profiler les deux divisions par rapport à l'expression rationnelle que vous utilisiez. Je ne suis pas sûr de savoir lequel serait le plus rapide. Souvenez-vous également que vous pouvez affecter une tranche de tableau: 'my @row; @row [ANNÉE, LUN, JOUR, OUVRIR, AD_CLOSE] = m /.../; return \ @row; ' – cjm

+1

Un conseil pour accélérer la version regex: ne capturez pas les champs que vous n'utilisez pas (comme' $ high'). Chaque ensemble de parens ralentit votre regex, car Perl doit copier ce bit de la chaîne dans $ 1, etc. – cjm

4
  1. Les hasards ne sont probablement pas aussi lents ou inutiles que vous le pensez.
  2. L'approche POE:
    use constant (YEAR => 0, MON => 1, DAY => 2, ...);
    $trade_ref->[YEAR] etc.
0

Il pourrait également être utile d'examiner si vous pouvez cacher le contenu du tableau dans une référence de tableau béni. Avoir une classe de trades, pousser des objets array sur votre tableau @trades et avoir une méthode pour retourner les données et le prix cacheront certains des problèmes que vous avez avec la duplication de code mais au risque d'un temps d'exécution légèrement plus lent à cause de la méthode appels. Un appel direct dans le tableau en utilisant une constante nommée sera plus rapide et je suppose que c'est plus important que toute autre chose.

1

Un tableau béni ne va pas être beaucoup plus chargé qu'un tableau non béni.

package Trade; 
use strict; 
use warnings; 
use English qw<@LAST_MATCH_START @LAST_MATCH_END>; 

my @slots = qw<year month day open high low close vol ad_close drop>; 
my %slot_for 
    = ((map { $slots[$_] => $_ } 0..$#slots) 
     , (map { $_ => -1 } qw<date>) 
    ) 
    ; 

foreach my $i (0..$#slots) { 
    my $name = $slots[$i]; 
    no strict 'refs'; 
    *$name = sub { 
     my ($self, $value) = @_; 
     my $slotr = \$self->[$i]; 
     return $$slotr unless $#_; 
     my $old = $$slotr; 
     $$slotr = $value; 
     return $$slotr; 
    }; 
} 

my @trades; 
my %format_for; 

sub trades { return @{[ @trades ]} }; 

sub new { 
    my $class = shift; 
    my @args = @_; 

    if (@args == 0) { 
     $args[0] = $_; 
    } 
    if (@args == 1) { 
     my $line = shift @args; 
     @args 
      = $line =~ 
       m/^(\d+)-(\d+)-(\d+), #date YYYY-MM-DD 
       (\d+\.\d+),  #open 
       (\d+\.\d+),  #High 
       (\d+\.\d+),  #Low 
       (\d+\.\d+),  #Close 
       (\d+),    #Vol 
       (\d+\.\d+) 
       /x 
      ; 
     my ($open, $ad_close) = @args[3,8]; 
     push @args, ($ad_close - $open)/$open; 
    } 

    my $self = bless \@args, $class; 
    push @trades, $self; 
    return $self; 
} 

sub format { 
    my $self = shift; 
    my $format = shift; 
    my $format_ref = $format_for{ $format }; 
    unless ($format_ref) { 
     my @format_list; 
     my $fmt = $format; 
     while ($fmt =~ m/\$(\w+)/g) { 
      next unless exists $slot_for{ $1 }; 
      push @format_list, \&$1; 
      substr($fmt, $LAST_MATCH_START[0], $LAST_MATCH_END[0] - $LAST_MATCH_START[0], '%s'); 
      pos($fmt) = $LAST_MATCH_START[0] + 2; 
     } 
     $fmt =~ s/\\n/\n/gm; 
     $format_ref 
      = $format_for{ $format } 
      = { format => $fmt, list => \@format_list } 
      ; 
    } 
    return $format unless $format_ref->{list}; 
    my ($fmt, $format_list) = @$format_ref{ qw<format list> }; 
    return sprintf($fmt, map { $_->($self) } @$format_list); 
} 

sub date { 
    my $str = join('-', &year, &month, &day); 
    return $str; 
} 

package main; 

Trade->new while <DATA>; 

print $_->format('$date $open,$ad_close,$drop\n') foreach Trade->trades(); 
__DATA__ 
2010-10-08,10948.50,11055.29,10901.12,11006.48,3871420000,11006.48 
2010-10-07,10968.41,11032.17,10878.04,10948.58,3910550000,10948.58 
2010-10-06,10936.79,11015.86,10880.08,10967.65,4073160000,10967.65 
2010-10-05,10752.63,10982.98,10752.63,10944.72,4068840000,10944.72 
2010-10-04,10828.85,10875.54,10682.66,10751.27,3604110000,10751.27 
2010-10-01,10789.72,10907.41,10759.14,10829.68,4298910000,10829.68 
2010-09-30,10835.96,10960.99,10732.27,10788.05,4284160000,10788.05 
2010-09-29,10857.98,10901.96,10759.75,10835.28,3990280000,10835.28 
2010-09-28,10809.85,10905.44,10714.03,10858.14,4025840000,10858.14 
2010-09-27,10860.03,10902.52,10776.44,10812.04,3587860000,10812.04 
2010-09-24,10664.39,10897.83,10664.39,10860.26,4123950000,10860.26 
2010-09-23,10738.48,10779.65,10610.12,10662.42,3847850000,10662.42 
2010-09-22,10761.11,10829.75,10682.40,10739.31,3911070000,10739.31 
2010-09-21,10753.39,10844.89,10674.83,10761.03,4175660000,10761.03 
2010-09-20,10608.08,10783.51,10594.38,10753.62,3364080000,10753.62 
2010-09-17,10595.44,10689.29,10529.67,10607.85,4086140000,10607.85 
2010-09-16,10571.75,10624.58,10499.43,10594.83,3364080000,10594.83 
2010-09-15,10526.42,10609.21,10453.15,10572.73,3369840000,10572.73 
2010-09-14,10544.81,10622.69,10460.34,10526.49,4521050000,10526.49 
2010-09-13,10458.60,10605.73,10458.45,10544.13,4521050000,10544.13 
2010-09-10,10415.01,10502.80,10376.34,10462.77,3061160000,10462.77 
2010-09-09,10388.22,10515.86,10359.23,10415.24,3387770000,10415.24 
2010-09-08,10338.57,10460.50,10318.93,10387.01,3224640000,10387.01 
2010-09-07,10446.80,10448.99,10304.44,10340.69,3107380000,10340.69 
2010-09-03,10321.92,10484.71,10321.92,10447.93,3534500000,10447.93 
2010-09-02,10270.08,10350.98,10211.80,10320.10,3704210000,10320.10 
2010-09-01,10016.01,10305.87,10016.01,10269.47,4396880000,10269.47 
2010-08-31,10006.42,10101.53,9915.73,10014.72,4038770000,10014.72