Perl:从数组中提取值对

时间:2010-12-17 11:55:13

标签: perl while-loop

考虑

#!/usr/bin/perl
use strict;
use warnings;

while(<DATA>) {
  my($t1,$t2,$value);
  ($t1,$t2)=qw(A P); $value = $1 if /^$t1.*$t2=(.)/;
  ($t1,$t2)=qw(B Q); $value = $1 if /^$t1.*$t2=(.)/;
  ($t1,$t2)=qw(C R); $value = $1 if /^$t1.*$t2=(.)/;
  print "$value\n";
}

__DATA__
A P=1 Q=2 R=3
B P=8 Q=2 R=7
C Q=2 P=1 R=3

我想用优雅的循环代替存储在数组(或其他结构)中的$ t1,$ t2值,如

之一
my @pairs = qw (A,P   B,Q   C,R);
my @pairs = qw (A P   B Q   C R);

我在简短尝试合并whilesplitunshift方面没有取得多大成功。

我错过了什么简洁优雅的解决方案?


P.S。我过去使用过哈希,但发现%h = (A=>'P', B=>'Q', C=>'R')语法“嘈杂”。扩展到三胞胎,四边形也很难看......

5 个答案:

答案 0 :(得分:9)

当哈希+ each不够好时(因为

  • 对列表中的第一个元素不是唯一的,或
  • 您需要按特定顺序迭代对,或
  • 因为您需要抓取三个或更多元素而不是两个,或
  • ...),

List::MoreUtils::natatime方法:

use List::MoreUtils q/natatime/;

while(<DATA>) {
  my($t1,$t2,$value);
  my @pairs = qw(A P B Q C R);
  my $it = natatime 2, @pairs;
  while (($t1,$t2) = $it->()) {
      $value = $1 if /^$t1.*$t2=(.)/;
  }
  print "$value\n";
}

__DATA__
A P=1 Q=2 R=3
B P=8 Q=2 R=7
C Q=2 P=1 R=3

但是,通常情况下,我只会splice列出此列任务列表的前几个元素:

while(<DATA>) {
  my($t1,$t2,$value);
  my @pairs = qw(A P B Q C R);
  # could also say  @pairs = (A => P, B => Q, C => R);
  while (@pairs) {
      ($t1,$t2) = splice @pairs, 0, 2;
      $value = $1 if /^$t1.*$t2=(.)/;
  }
  print "$value\n";
}

答案 1 :(得分:4)

使用哈希。

my %map = ( A => 'P', B => 'Q', C => 'R' );

while (<DATA>) {
    my $re = substr($_, 0, 1) . ".*" . $map{ substr($_, 0, 1) } . "=(.)";
    /$re/;
    print "$1\n";
}

答案 2 :(得分:4)

除非您能保证第一个坐标始终是唯一的,否则一对的概念更好地表示为两个元素的单个数组。您也可以更容易地将相同的想法扩展到更高维度的元组。

#!/usr/bin/perl
use strict; use warnings;
use Data::Dumper;

my @tuples = ([qw(A P)],  [qw(B Q)], [qw(C R)]);
my $re_tmpl = '^%s.*%s=(.)';
my @re = map qr/$_/, map sprintf($re_tmpl, @$_), @tuples;

while (my $line = <DATA>) {
    last unless $line =~ /\S/;

    my ($value) = map { $line =~ $_ } @re;

    print $value, "\n";
}

__DATA__
A P=1 Q=2 R=3
B P=8 Q=2 R=7
C Q=2 P=1 R=3

但是,使用上面的方法和方法,您执行的匹配操作比必要的多(每行三个而不是一个)。这使得@eugene's answer更有效率。

更通用的解决方案是使用:

#!/usr/bin/perl
use strict; use warnings;

my @tuples = ([qw(A P)],  [qw(B Q)], [qw(C R)]);
my $re_tmpl = '^%s.*%s=(.)';

my %re;
@re{ map $_->[0], @tuples } = map qr/$_/,
                              map sprintf($re_tmpl, @$_),
                              @tuples;

while (my $line = <DATA>) {
    last unless $line =~ /\S/;

    my ($value) = $line =~ $re{substr $line, 0, 1};

    print $value, "\n";
}

__DATA__
A P=1 Q=2 R=3
B P=8 Q=2 R=7
C Q=2 P=1 R=3

关于这一点的好处是你可以适应尺寸大于2的元组。

此外,既然您正在根据线条的第一个字符选择图案,那么图案本身就会变得更简单:

#!/usr/bin/perl
use strict; use warnings;

my @tuples = ([qw(A P)],  [qw(B Q)], [qw(C R)]);
my $re_tmpl = '%s=(.)';

my %re;
@re{ map $_->[0], @tuples } = map qr/$_/,
                              map sprintf($re_tmpl, $_->[1]),
                              @tuples;

while (my $line = <DATA>) {
    last unless $line =~ /\S/;

    my ($value) = $line =~ $re{substr $line, 0, 1};

    print $value, "\n";
}

__DATA__
A P=1 Q=2 R=3
B P=8 Q=2 R=7
C Q=2 P=1 R=3

一个更简单的替代方案(需要捕获所有x=y)是:

#!/usr/bin/perl
use strict; use warnings;

my %pairs = qw(A P B Q C R);
my $re = qr/([A-Z])=([0-9])/;

while (my $line = <DATA>) {
    last unless $line =~ /\S/;

    my $type = substr $line, 0, 1;

    my $value = { $line =~ /$re/g }->{ $pairs{$type} };

    print "$value\n";

}

__DATA__
A P=1 Q=2 R=3
B P=8 Q=2 R=7
C Q=2 P=1 R=3

最后一个也可以轻松从一行中提取多个值:

#!/usr/bin/perl
use strict; use warnings;

my %tuples = (A => [qw(P Q)], B => [qw(Q R)], C => [qw(P R)]);
my $re = qr/([A-Z])=([0-9])/;

while (my $line = <DATA>) {
    last unless $line =~ /\S/;

    my $type = substr $line, 0, 1;

    my @values = @{ { $line =~ /$re/g } }{ @{ $tuples{$type} } };

    print "@values\n";
}

__DATA__
A P=1 Q=2 R=3
B P=8 Q=2 R=7
C Q=2 P=1 R=3

答案 3 :(得分:3)

扩展我的评论。

#!/usr/bin/perl
use strict;
use warnings;

my %pairs = qw/A P   B Q   C R/;

foreach my $data (<DATA>) {
    while(my($t1, $t2) = each(%pairs)){
        $data =~ /^$t1.*$t2=(.)/ && print "$1\n";
    }
}

答案 4 :(得分:1)

Elsewhere,Tad McLellan观察到数据看起来像是HoH并建议:

my %pairs = qw/A P   B Q   C R/;

while (<DATA>) {
    my($type, %values) = split /[\s=]/;
    print "$values{$pairs{$type}}\n";
}