我需要构建以下流程:
- 接受文件名列表
- 从这些文件中提取多行
- 处理
然而,我不知道如何正确地将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
语句前缀是很好的工具。
最后,关于你发布的代码的几个小注意事项与你的问题无关,但可能会有所帮助:
- 所有的Raku函数都返回它们的最终表达式,所以
parse-file
中的return
语句是不必要的(而且它实际上稍微慢一些/不习惯)。 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 );