【问题标题】:Reading file line by line in Perl6, how to do idiomatically?在Perl6中逐行读取文件,如何惯用?
【发布时间】:2019-09-03 17:05:26
【问题描述】:

我在 Perl6 中有一个基本脚本,它运行非常很慢,比精确的 perl5 翻译慢大约 30 倍。

CONTROL {
    when CX::Warn {
        note $_;
        exit 1;
    }
}
use fatal;
role KeyRequired {
    method AT-KEY (\key) {
        die "Key {key} not found" unless self.EXISTS-KEY(key);
        nextsame;
    }
}

for dir(test => /^nucleotide_\d**2_\d**2..3\.tsv$/) -> $tsv {
    say $tsv;
    my $qqman = $tsv.subst(/\.tsv$/, '.qqman.tsv');
    my $out = open $qqman, :w;
    put "\t$qqman";
    my UInt $line-no = 0;
    for $tsv.lines -> $line {
        if $line-no == 0 {
            $line-no = 1;
            $out.put(['SNP', 'CHR', 'BP', 'P', 'zscore'].join("\t"));
            next
        }
        if $line ~~ /.+X/ {
            next
        }
        $line-no++;
        my @line = $line.split(/\s+/);
        my $chr = @line[0];
        my $nuc = @line[1];
        my $p = @line[3];
        my $zscore = @line[2];
        my $snp = "'rs$line-no'";
        $out.put([$snp, $chr, $nuc, $p, $zscore].join("\t"));
        #$out.put();
    }
    last
}

这是 Perl5 的 while 的惯用语。

这是一个非常简单的脚本,它只改变文件中的文本列。这个 Perl6 脚本在 30 分钟内运行。 Perl5 翻译运行时间为 1 分钟。

我已尝试阅读 Using Perl6 to process a large text file, and it's Too Slow.(2014-09)Perl6 : What is the best way for dealing with very big files?,但在这里我没有看到任何可以帮助我的东西 :(

我正在运行Rakudo version 2018.03 built on MoarVM version 2018.03 implementing Perl 6.c.

我意识到 Rakudo 还没有成熟到 Perl5 的水平(我希望如此),但是我怎样才能让它在更合理的时间范围内逐行读取文件?

【问题讨论】:

  • 是什么让您认为逐行读取文件是脚本的瓶颈?
  • @ugexe 数学很简单。否则我没想到会花很长时间。但是,我将精简脚本以验证逐行实际上是瓶颈。
  • 不仅仅是简单的数学运算。有 IO(打开/读取文件)、赋值、正则表达式解析和类型约束检查。
  • 看来您可以将 $line ~~ /.+X/ 更改为 $line.index("X"),因为您没有捕获任何内容。
  • 如果您处理多个文件,您可以尝试通过将 for 更改为 map 来并行处理它们,然后使用 hyper or race

标签: raku


【解决方案1】:

我会改变很多事情。

  • /.+X/ 可以简化为 /.X/ 甚至 $line.substr(1).contains('X')
  • $line.split(/\s+/) 可以简化为 $line.words
  • $tsv.subst(/\.tsv$/, '.qqman.tsv') 可以简化为 $tsv.substr(*-4) ~ '.qqman.tsv'
  • uint 而不是 UInt
  • given .head {} 而不是 for … {last}
given dir(test => /^nucleotide_\d**2_\d**2..3\.tsv$/).head -> $tsv {
    say $tsv;
    my $qqman = $tsv.substr(*-4) ~ '.qqman.tsv';
    my $out = open $qqman, :w;
    put "\t$qqman";

    my uint $line-no = 0;
    for $tsv.lines -> $line {
        FIRST {
            $line-no = 1;
            $out.put(('SNP', 'CHR', 'BP', 'P', 'zscore').join("\t"));
            next
        }
        next if $line.substr(1).contains('X');

        ++$line-no;

        my ($chr,$nuc,$zscore,$p) = $line.words;

        my $snp = "'rs$line-no'";
        $out.put(($snp, $chr, $nuc, $p, $zscore).join("\t"));
        #$out.put();
    }
}

【讨论】:

  • if $line ~~ /.X/ { 确实是瓶颈。令人惊讶的是,删除 + 会从 30 分钟缩短到 9 分钟。 $line.substr(1).contains('X') 不太一样,因为我认为 X 可能排在最前面?行前的 X 是可以接受的。
  • @con: /.+X/ /.X/$line.substr(1).contains('X') 在功能上是相同的。 /.*X/ /X/$line.contains('X') 在功能上是相同的。 (.substr(1) 与行首的X 不匹配。)
  • 虽然 OP 主要关注速度,但他们也简要提到了惯用性,因此对于我正在打开的文件逐行处理,我一直在寻找格式 for $tsv.IO.lines -> $line 到是一个更好的方式。 $out.put: <SNP CHR BP P zscore>.join("\t") 同上,尽管功能相同。
猜你喜欢
  • 2019-05-29
  • 1970-01-01
  • 1970-01-01
  • 2011-11-02
  • 2020-01-29
  • 2019-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多