如何以懒惰的方式将采集滑动到地图中?



我需要构建以下流程:

  • 接受文件名列表
  • 从这些文件中提取多行
  • 处理

然而,我不知道如何正确地将gather-take注入map:

sub MAIN ( *@file-names ) {
@file-names.map( { slip parse-file( $_ ) } ).map( { process-line( $_ ) } );
}
sub parse-file ( $file-name ) {
return gather for $file-name.IO.lines -> $line {
take $line if $line ~~ /a/; # dummy example logic
}
}
sub process-line ( $line ) {
say $line;  # dummy example logic
}

这段代码可以工作,但是会疯狂地泄漏内存。我想slip会让gather-take产生渴望吧?或者slip没有将Seq项目标记为已消耗?是否有一种方法可以将gather-take的结果以懒惰的方式滑入map?

BTW:我的意图是并行化race的每一步-所以,例如,我有2个文件在同一时间为10行处理器生成行解析。一般来说,我试图找出组合这种级联流的最简单方法。我尝试过通道连接每个处理步骤,但它们没有内置的回推。如果您对此类流有任何其他模式,那么欢迎评论。

编辑1:

我认为我的代码是正确的,内存泄漏不是由坏逻辑引起的,而是由Slip类中的错误引起的。我已经创建了问题https://github.com/rakudo/rakudo/issues/5138,目前是开放的。一旦解决了,我将发布更新。

编辑2:不,我的代码不正确:)检查我的帖子的答案。

我相信您对代码中非惰性的原因是错误的-一般来说,使用slip通常不会使代码急于。实际上,当我运行下面所示的稍微修改过的代码时:

sub MAIN () {
my @file-names = "tmp-file000".."tmp-file009";
spurt $_, ('a'..'z').join("n") for @file-names;
my $parsed = @file-names.map( { slip parse-file( $_ ) } );
say "Reached line $?LINE";
$parsed.map( { process-line( $_ ) } );
}
sub parse-file ( $file-name ) {
say "processing $file-name...";
gather for $file-name.IO.lines -> $line {
take $line if $line ~~ /a/; # dummy example logic
}
}
sub process-line ( $line ) {
say $line;  # dummy example logic
}

我得到的输出显示Raku惰性地处理文件(注意,在需要将新值传递给process-line之前,它不会调用parse-file):

Reached 8
processing tmp-file000...
a
processing tmp-file001...
a
processing tmp-file002...
a
processing tmp-file003...
a
processing tmp-file004...
a
processing tmp-file005...
a
processing tmp-file006...
a
processing tmp-file007...
a
processing tmp-file008...
a
processing tmp-file009...
a

因为我没有剩下的代码,我不确定是什么触发了你观察到的非懒惰行为。一般来说,如果你的代码在你想让它变懒的时候被急切地求值,那么.lazy方法和/或lazy语句前缀是很好的工具。


最后,关于你发布的代码的几个小注意事项与你的问题无关,但可能会有所帮助:

  1. 所有的Raku函数都返回它们的最终表达式,所以parse-file中的return语句是不必要的(而且它实际上稍微慢一些/不习惯)。
  2. gather/take的强大之处在于它们可以跨越功能边界。也就是说,你可以有一个parse-file函数,take有不同的行,而不需要在parse-lines中有gather语句——你只需要在gather块的范围内调用parse-lines。这个感觉可能有助于解决您正在处理的问题,尽管没有更多信息很难确定。

首先,我有一个很大的误解。我认为parse-file生成的所有行都必须像这样滑进地图block:

@file-names.map( produce all lines here ).map( process all lines here );

Slip是一个跟踪所有元素的List。这就是为什么我有很大的内存泄漏。

解决方案是在map内创建gather-take序列,但在map外使用它:

@file-names.map( { parse-file( $_ ) } ).flat.map( { process-line( $_ ) } );

所以现在是:

@file-names.map( construct sequence here ).(get items from sequence here).map( process all lines here );

最新更新