【问题标题】:Perl: Template for an anonymous array?Perl:匿名数组的模板?
【发布时间】:2011-04-24 03:51:37
【问题描述】:

我有一个 Perl 脚本来逐行分析数兆字节的数据。

例如,我将使用道琼斯平均指数的收盘价来计算几条线。

数据被读取,文件中数据的布局很简单。在这个例子中,它是:

Date Open High Low Close Volume Adj-Close

当我读取数据时,会执行许多计算。一些数据和计算被保留以备后用。假设新数据是:

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

下面是一些示例代码:

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)=@$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

在代码中标记为#HERE 的地方,请注意,我首先将一个匿名数组推送到另一个命名数组以供以后访问。后来(在实际程序中很晚)我通过引用访问同一个数组。

到目前为止,我刚刚从第一个 # HERE 中剪切了“模板”[$year,$mon,$day,$open,$ad_close,$drp] 的文本,并将my ($year,$mon,$day,$open,$ad_close,$drp)=@$trade_ref; 手动粘贴到程序中的另一个 # HERE 中。必须是更好的方法...

有没有一种方法可以让我推送一个匿名数组的模板,以便在其他情况下通过脚本自动有序地引用它?在实际脚本中,$drp 等分析结果发生了变化,我希望数据的创建发生变化,即使我不更改 my ($year,$mon,$day,$open,$ad_close,$drp)=@$trade_ref; 也可以由以后的例程优雅地处理如果这是一个 C 程序,则定义可以是一个地方的宏...

我正在考虑推送一个匿名哈希而不是一个数组,其中哈希具有名称/值对,该对具有值的名称,然后是该值的值。从理论上讲,我知道这会奏效,但让我觉得浪费和缓慢。实际数据集中大约有 2 GB 的数据,我可以使用当前设计快速完成。

有没有更好的方法?

【问题讨论】:

  • 变量声明和赋值可以节省一行重复代码:if (my ($year, …) = …)

标签: arrays perl hash


【解决方案1】:
  1. Hashref 可能并不像您想象的那么慢或那么浪费。
  2. POE 方法:
    use constant (YEAR =&gt; 0, MON =&gt; 1, DAY =&gt; 2, ...);
    $trade_ref-&gt;[YEAR]

【讨论】:

    【解决方案2】:
    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
    

    【讨论】:

    • 我认为这是一个好的开始,让我思考。使用use constant,我可以修改返回以具有相同的行为并实现我所描述的我认为...+1,谢谢!
    • 我将分析两个拆分与您一直使用的单个正则表达式。我不确定哪个会更快。另外,请记住,您可以分配给数组切片:my @row; @row[YEAR, MON, DAY, OPEN, AD_CLOSE] = m/.../; return \@row;
    • 加快正则表达式版本的一个提示:不要捕获您不会使用的字段(例如$high)。每组括号都会减慢您的正则表达式,因为 Perl 必须将字符串的该位复制到 $1 等中。
    【解决方案3】:

    您是否可以将数组的内容隐藏在受祝福的数组引用中也可能值得研究。拥有一个交易类,将数组对象推送到您的@trades 数组,然后拥有一个返回数据的方法,价格将隐藏您在代码重复方面遇到的一些问题,但由于该方法,运行时间可能会稍微变慢来电。使用命名常量直接调用数组会更快,我认为这比其他任何事情都重要。

    【讨论】:

      【解决方案4】:

      祝福数组不会比未祝福数组负载那么多。

      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
      

      【讨论】:

        猜你喜欢
        • 2014-03-16
        • 2012-05-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-17
        • 1970-01-01
        • 2013-02-28
        相关资源
        最近更新 更多