【问题标题】:reading columns from Hash of Arrays从数组哈希中读取列
【发布时间】:2012-10-07 20:49:20
【问题描述】:

我是 perl 新手,有一个关于使用数组哈希检索特定列的问题。我的代码如下:

my %hash = ( name1 => ['A', 'A', 'B', 'A', 'A', 'B'],
             name2 => ['A', 'A', 'D', 'A', 'A', 'B'],
             name3 => ['A', 'A', 'B', 'A', 'A', 'C'],
             );

#the values of %hash are returned as arrays not as string (as I want)

foreach my $name (sort keys %hash ) {
    print "$name: ";
    print "$hash{$name}[2]\n";
}

for (my $i=0; $i<$length; $i++) {
        my $diff = "no";
        my $letter = '';
        foreach $name (sort keys %hash) {
            if (defined $hash{$name}[$i]) {
                if ($hash{$name}[$i] =~ /[ABCD]/) {
                    $letter = $hash{$name}[$i];
                }
                elsif ($hash{$name}[$i] ne $letter) { 
                    $diff = "yes";
                }
            }
            if ( $diff eq "yes" ) {
                foreach $name (sort keys %hash) {
                    if (defined $hash{$name}[$i]) { $newhash{$name} .= $hash{$name}[$i]; }  
                }
            }
        }
    }
    foreach $name (sort keys %newhash ) {
        print "$name: $newhash{$name} \n";
    }

我希望这个程序的输出类似于只有变量列的新哈希:

my %newhash = ( name1 => 'BB',
            name2 => 'DB',
            name3 => 'BC',
              );

但只收到此消息: 在 test_hash.pl 第 31 行的字符串 ne 中使用未初始化的值 $letter。

有人对此有想法吗? 干杯

编辑:

非常感谢您在这个问题上的帮助。

我编辑了我的帖子以确认 frezik、Dan1111、Jean 的建议。你是对的,现在没有警告,但我也无法从 print 语句中获得任何输出,我对此一无所知......

@TLP:好的,我只是生成一组随机的列,没有任何排序目的。我真正想要的是字母如何变化,这意味着如果对于相同的数组索引(存储在哈希中)字母相同,则丢弃它们,但如果键之间的字母不同,我想存储该索引新哈希中的列。

干杯。

【问题讨论】:

  • 您对“变量列”的定义是什么。看起来您想要除 A 之外的所有字母。或者可能是第 3 列和第 6 列中的字母。或每三个字母。但是从您的代码中,我以某种方式得到的印象是字母如何变化。那么它是什么?
  • 好的,所以你想比较不同的数组,如果一列是“AAA”或“BBB”,跳过它。但是,您必须先保存这些字母,然后再进行检查。
  • 没错,但我认为我将字母保存在变量 $letter 中。但是,似乎并没有将它们添加到新的哈希中...

标签: perl hash perl-data-structures


【解决方案1】:

我假设你想匹配任何字母 A、B、C 或 D:

if ($hash{$name}[$i] =~ /ABCD/)

但是,正如所写,它与确切的字符串“ABCD”匹配。你需要一个你想要的字符类:

if ($hash{$name}[$i] =~ /[ABCD]/)

但是,您也有其他逻辑问题,可能会导致您在设置之前与$letter 进行比较。将其设置为空(如 Jean 建议的那样)是一个简单的选项,可能会有所帮助。

这里还有一个问题:

print "$name: @{ $newhash{$name} }\n";

%newhash不是数组的hash,所以需要去掉数组解引用:

print "$name: $newhash{$name} \n";

【讨论】:

    【解决方案2】:

    您可能对此替代解决方案感兴趣

    use strict;
    use warnings;
    
    my %hash = (
      name1 => ['A', 'A', 'B', 'A', 'A', 'B'],
      name2 => ['A', 'A', 'D', 'A', 'A', 'B'],
      name3 => ['A', 'A', 'B', 'A', 'A', 'C'],
    );
    
    my @columns;
    
    for my $list (values %hash) {
      $columns[$_]{$list->[$_]}++ for 0 .. $#$list;
    }
    
    my %newhash = %hash;
    
    for my $list (values %newhash) {
      $list = join '', map $list->[$_], grep keys %{$columns[$_]} > 1, 0 .. $#$list;
    }
    
    use Data::Dump;
    dd \%newhash;
    

    输出

    { name1 => "BB", name2 => "DB", name3 => "BC" }
    

    【讨论】:

      【解决方案3】:

      我认为逐个检查字母是错误的。收集所有信件并立即检查它们似乎更容易。 List::MoreUtils 模块的 uniq 函数可以快速确定字母是否变化,并且可以轻松地将它们转换为生成的哈希。

      use strict;
      use warnings;
      use Data::Dumper;
      use List::MoreUtils qw(uniq);
      
      my %hash = ( name1 => ['A', 'A', 'B', 'A', 'A', 'B'],
                   name2 => ['A', 'A', 'D', 'A', 'A', 'B'],
                   name3 => ['A', 'A', 'B', 'A', 'A', 'C'],
      );
      my @keys = keys %hash;
      my $len = $#{ $hash{$keys[0]} };   # max index
      my %new;
      
      for my $i (0 .. $len) {
          my @col;
          for my $key (@keys) {
              push @col, $hash{$key}[$i];
          }
          if (uniq(@col) != 1) {     # check for variation
              for (0 .. $#col) {
                  $new{$keys[$_]} .= $col[$_];
              }
          }
      }
      print Dumper \%new;
      

      输出:

      $VAR1 = {
                'name2' => 'DB',
                'name1' => 'BB',
                'name3' => 'BC'
              };
      

      【讨论】:

        【解决方案4】:

        您的标量 $letter 未定义。添加这个以消除警告。

        my $letter='';
        

        【讨论】:

          【解决方案5】:
          if ($hash{$name}[$i] =~ /ABCD/) {
          

          上面的正则表达式将匹配__ABCD__ABCD1234 之类的字符串,但绝不会匹配单独的AB。您可能想要匹配其中任何一个字母,并且锚定正则表达式也是一个好主意:

          if ($hash{$name}[$i] =~ /\A [ABCD] \z/x) {
          

          (/x 选项表示忽略空格,这有助于使正则表达式更易于阅读。)

          $i == 2 并且内部循环碰巧首先按下键name1name3 时,您仍然会在上面的示例中收到警告。由于正则表达式与 T 不匹配,$letter 将保持未初始化状态。

          【讨论】:

            【解决方案6】:

            太好了。非常感谢您在这个问题上的所有帮助。

            我根据 TLP 的建议尝试了一个代码,效果很好。因为我对 perl 比较陌生,所以我认为这段代码比 Borodin 的代码更容易理解。我所做的是:

            #!/usr/bin/perl
            use strict;
            use warnings;
            use List::MoreUtils qw(uniq);
            
            my %hash = ( name1 => ['A', 'A', 'T', 'A', 'A', 'T', 'N', 'd', 'd', 'D', 'C', 'T', 'T', 'T'],
                     name2 => ['A', 'A', 'D', 'A', 'A', 'T', 'A', 'd', 'a', 'd', 'd', 'T', 'T', 'C'],
                     name3 => ['A', 'A', 'T', 'A', 'A', 'C', 'A', 'd', 'd', 'D', 'C', 'T', 'C', 'T'],
            );
            my @keys = keys %hash;
            my $len = $#{ $hash{$keys[0]} };   # max index
            my %new;
            
            for (my $i=0; $i<$length; $i++) {
                my @col;
                for my $key (@keys) {
                   if ($hash{$key}[$i] =~ /[ABCDT]/) {     #added a pattern match
                        push @col, $hash{$key}[$i];
                   }
                }
                if (uniq(@col) != 1) {     # check for variation
                    for (0 .. $#col) {
                        $new{$keys[$_]} .= $col[$_];
                    }
                }
            }
            foreach my $key (sort keys %new ) {
                print "$key: $new{$key}\n";
            }
            

            但是,在使用 uniq 函数时(if (uniq(@col) == 1)),我注意到输出有点错误:

            name1: AAAAADCT
            name2: AAAAADCT
            name3: AAAAT
            

            似乎没有保留键 => 值的初始顺序。有人对此有提示吗?

            干杯。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2017-01-24
              • 2013-01-23
              • 1970-01-01
              • 2012-08-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多