Raku中如何实现与Haskell span函数功能等价的子程序
Raku 实现等价于 Haskell
span 函数的方案 Haskell 原生的span函数接收一个谓词、一个列表作为入参,会将列表拆分为两个部分:
- 列表开头连续满足谓词条件的最长前缀元素序列
- 拆分后剩余的列表后缀部分
以Haskell官方示例为例:
span (< 10) [2, 2, 2, 5, 5, 7, 13, 9, 6, 2, 20, 4]
求值返回结果为:
([2,2,2,5,5,7],[13,9,6,2,20,4])
基础实现版本
最初的实现版本对传入的谓词做了取反处理,严格对齐Haskell span的正向判定逻辑:
sub span( &predicate, @numberList ) { my &negatedPredicate = { ! &predicate($^x) } ; my $idx = @numberList.first(&negatedPredicate):k ; my @lst is Array[List] = @numberList[0..$idx-1], @numberList[$idx..*] ; @lst ; } # end sub span sub MAIN() { my &myPredicate = { $_ <= 10 } ; my @myNumberList is Array[Int] = [2, 2, 2, 5, 5, 7, 13, 9, 6, 2, 20, 4] ; my @result is Array[List] = span( &myPredicate, @myNumberList ) ; say '@result is ...' ; say @result ; say '@result[0] is ...' ; say @result[0] ; say @result[0].WHAT ; say '@result[1] is ...' ; say @result[1] ; say @result[1].WHAT ; } # end sub MAIN
基础版本的运行输出如下:
@result is ... [(2 2 2 5 5 7) (13 9 6 2 20 4)] @result[0] is ... (2 2 2 5 5 7) (List) @result[1] is ... (13 9 6 2 20 4) (List)
覆盖边界场景的最终版本
基础版本没有处理「列表所有元素都满足谓词」的边界情况:此时first方法找不到匹配的、不满足谓词的元素,会返回Nil,导致数组切片逻辑出错。结合Raku Nil类型的处理规则修复后,最终可以100%对齐Haskell span行为的实现如下:
sub span( &predicate, @numberList ) { my &negatedPredicate = { ! &predicate($^x) } ; my $idx = @numberList.first( &negatedPredicate ):k ; if Nil ~~ any($idx) { $idx = @numberList.elems ; } my List $returnList = (@numberList[0..$idx-1], @numberList[$idx..*]) ; $returnList ; } # end sub span sub MAIN() { say span( { $_ == 0 }, [2, 2, 5, 7, 4, 0] ) ; # (() (2 2 5 7 4 0)) say span( { $_ < 6 }, (2, 2, 5, 7, 4, 0) ) ; # ((2 2 5) (7 4 0)) say span( { $_ != 9 }, [2, 2, 5, 7, 4, 0] ) ; # ((2 2 5 7 4 0) ()) } # end sub MAIN
该版本覆盖了全部三类使用场景:
- 首元素就不满足谓词时,返回空前缀 + 完整原列表
- 列表中间存在不满足谓词的元素时,以第一个不满足元素的位置为界拆分
- 所有元素都满足谓词时,返回完整原列表 + 空后缀
行为与Haskell原生span完全一致。
内容的提问来源于stack exchange,提问作者user3134725
相关产品推荐
相关产品推荐

