在Perl

时间:2017-05-19 05:59:44

标签: perl

我希望使用子例程将制表符分隔的文本文件解析为嵌套哈希。每个文件行将由来自uid列的唯一ID键控,标题行作为嵌套键。哪个(哪些)列成为uid更改(因为有时没有唯一的列,因此uid必须是列的组合)。我的问题是$uid变量,我将其作为非插值字符串传递。当我尝试以插值的方式在子程序中使用它时,它只会给我非插值的值:

    use strict;
    use warnings;

    my $lofrow = tablehash($lof_file, '$row{gene}', "transcript", "ENST");

    ##sub to generate table hash from file w/ headers
    ##input values are file, uid, header starter, row starter, max column number
    ##returns hash reference (deref it)
    sub tablehash   { 
        my ($file, $uid, $headstart, $rowstart, $colnum) = @_;
        if (!$colnum){ # takes care of a unknown number of columns
            $colnum = 0;
        }
        open(INA, $file) or die "failed to open $file, $!\n";
        my %table; # permanent hash table 
        my %row; # hash of column values for each row
        my @names = (); # column headers
        my @values = (); # line/row values
        while (chomp(my $line = <INA>)){ # reading lines for lof info
            if ($line =~ /^$headstart/){
                @names = split(/\t/, $line, $colnum);
            } elsif ($line =~ /^$rowstart/){ # splitting lof info columns into variables
                @values = split(/\t/, $line, $colnum);
                @row{@names} = @values;
                print qq($uid\t$row{gene}\n); # problem: prints "$row{gene} ACB1"
                $table{"$uid"} = { %row }; # puts row hash into permanent hash, but with $row{gene} key)
            }
        }
        close INA;
        return \%table;
    }

我没有想法。我可以放$table{$row{$uid}}并简单地传递"gene",但在一些情况下,我希望$uid "$row{gene}|$row{rsid}"生成$table{ACB1|123456}

1 个答案:

答案 0 :(得分:3)

插值是Perl 解析器的一个功能。当你写像

这样的东西
"foo $bar baz"

,Perl将其编译为类似

的内容
'foo ' . $bar . ' $baz'

它不会在运行时解释数据。

你所拥有的是一个字符串,其中一个字符恰好是$但没有特殊效果。

至少有两种方法可以做你想要的事情。其中之一是使用函数,而不是字符串。 (这是有道理的,因为插值实际上意味着在运行时连接,并且传递代码的方法是将其包装在函数中。)

my $lofrow = tablehash($lof_file, sub { my ($row) = @_; $row->{gene} }, "transcript", "ENST");

sub tablehash   { 
    my ($file, $mkuid, $headstart, $rowstart, $colnum) = @_;    
    ...
                my $uid = $mkuid->(\%row);
                $table{$uid} = { %row };

这里$mkuid不是字符串,而是对函数的引用(给定哈希引用)返回一个uid字符串。 tablehash调用它,将%row的引用传递给它。然后您可以将其更改为例如。

my $lofrow = tablehash($lof_file, sub { my ($row) = @_; "$row->{gene}|$row->{rsid}" }, "transcript", "ENST");

另一种解决方案是使用相当于模板字符串的内容:

my $lofrow = tablehash($lof_file, "gene|rsid", "transcript", "ENST");

sub tablehash   { 
    my ($file, $uid_template, $headstart, $rowstart, $colnum) = @_;    
    ...
                (my $uid = $uid_template) =~ s/(\w+)/$row{$1}/g;
                $table{$uid} = { %row };

s///代码遍历模板字符串,并使用%row中的相应值手动替换每个单词。

随机笔记:

  • 使用strictwarnings的加分点。
  • if (!$colnum) { $colnum = 0; }可简化为$colnum ||= 0;
  • 使用词法变量代替裸字文件句柄。 Barewords实际上是全局变量(并且语法上很尴尬,因为他们不是该语言的一等公民)。
  • 始终使用3参数形式的open来避免意外解释第二个参数。
  • 在错误消息中包含您的程序名称(明确地使用$0或隐式地忽略\n中的die)。
  • my @foo = (); my %bar = ();是多余的,可以简化为my @foo; my %bar;。数组和哈希开始是空的;用空列表覆盖它们是没有意义的。
  • 当你到达EOF时,
  • chomp(my $line = <INA>)会发出警告(因为你正试图扼杀包含undef的变量。)
  • my %row;应该在循环中声明。看起来它应该只包含当前行的值。

建议:

open my $fh, '<', $file or die "$0: can't open $file: $!\n";
while (my $line = readline $fh) {
    chomp $line;
    ...
}