Supply与普通Array中map函数处理Empty值的行为是否不一致?
Supply与Array中map处理Empty值的行为差异
近期我在学习Swift的Combine框架,苹果官方说明:
Combine框架为应用处理事件提供了声明式方法。无需实现多个委托回调或完成处理闭包,即可为指定事件源创建单一处理链,链中每个环节都是Combine操作符,对前一步接收的元素执行特定操作。
Combine中有一个compactMap函数,可过滤nil值,示例代码如下:
import Combine let strings = ["a", "1.24", "3", "def", "45", "0.23"].publisher strings .compactMap { Float($0) } .sink(receiveValue: { print($0) }) .store(in: &subscriptions)
我将上述代码用Raku改写后,发现运行结果不符合预期:
my @strings = ["a", "1.24", "3", "def", "45", "0.23"]; my Supply $supply = Supply.from-list(@strings); my Supply $compact = $supply.map(-> $value { try { Num($value) } if $! { Empty } else { Num($value) } }); # .grep(Num); $compact.tap( &say, done => { say 'finished' }, quit => { say 'done' } ); # 输出: # () # 1.24 # 3 # () # 45 # 0.23
但在普通Array上使用map处理Empty值时,Empty会被自动过滤:
my @strings = ["a", "1.24", "3", "def", "45", "0.23"]; my @compacted = @strings.map(-> $value { try { Num($value) } if $! { Empty } else { Num($value) } }); .say for @compacted; # 输出: # 1.24 # 3 # 45 # 0.23
结论:行为确实不一致
Array的map会自动展开Iterable类型的返回值(包括Empty),当返回Empty时,相当于当前元素被过滤,不会加入结果数组。
而Supply的map是将每个返回值直接作为事件发送,Empty在Supply中会被解析为空元组()并发送出去,不会被过滤。
正确实现Combine compactMap的Raku写法
要在Supply中实现类似compactMap的过滤效果,可以使用flat-map替代map:
my @strings = ["a", "1.24", "3", "def", "45", "0.23"]; my Supply $supply = Supply.from-list(@strings); my Supply $compact = $supply.flat-map(-> $value { try { Num($value) } if $! { Empty } else { Num($value) } }); $compact.tap( &say, done => { say 'finished' }, quit => { say 'done' } ); # 输出: # 1.24 # 3 # 45 # 0.23 # finished
flat-map会将返回的Iterable(比如Empty、列表)展开为单个事件,Empty不会产生任何事件,从而达到过滤无效值的效果,和Array的map行为一致。
也可以先转换值再过滤:
my @strings = ["a", "1.24", "3", "def", "45", "0.23"]; my Supply $supply = Supply.from-list(@strings); my Supply $compact = $supply.map(-> $value { try Num($value) }) .grep(-> $val { $val.defined && $val ~~ Num }); $compact.tap( &say, done => { say 'finished' }, quit => { say 'done' } );
内容的提问来源于stack exchange,提问作者chenyf
相关产品推荐
相关产品推荐

