我一直在使用.foldRight()
递归执行更高阶函数,例如any
,all
和takeWhile
,但dropWhile
已经难以捉摸。 _Collections.kt
有必要的方法,但我无法将其转换为递归结构。
供参考,这是takeWhile
fun takeWhile(list:List<Int>, func:(Int) -> Boolean):List<Int> = list.foldRight(emptyList(),
{ next:Int, acc:List<Int> -> if (func(next)) acc.plus(next) else emptyList() })
答案 0 :(得分:2)
首先,让我们概述解决方案的想法。
使用foldRight
,您只能从右到左逐个处理项目,维护累加器。
问题是,对于位置i
的项目,dropWhile
逻辑根据位置{{1处是否有项目 - 决定是否将项目包含到结果中不满足谓词的(如果是,则包括)。这意味着您不能简单地维护结果项:对于您已经处理的某些项目,您不知道它们是否应该实际包含在内。
示例:
(我们正在从右到左处理这些项目,因此我们不知道前缀)
j <= i
当我们在左侧发现更多项目时,有两种可能性:
我们找到了序列的开头,并且没有在谓词上提供... (some unknown items) ... ... ... ... a b c d <--- (right-to-left)
predicate satisfied: T T F T
的项目:
F
在这种情况下,应删除前缀 (the sequence start) y z a b c d <--- (right-to-left)
predicate satisfied: T T T T F T
-------
drop
。
我们发现了一个不符合谓词的项目:
y z a b
只有在此时我们才能确定我们需要包含项目... (some unknown items) ... w z a b c d <--- (right-to-left)
predicate satisfied: F T T T F T
-------
include
,我们之前无法做到这一点,因为可能会有序列的开头而不是项目w z a b
,然后我们应该放弃w
。
但请注意,在这两种情况下,我们都确定要将z a b
项添加到结果中:这是因为它们前面有c d
个c
谓词。< / p>
鉴于此,很明显,当从右到左处理项目时,您可以维护一个单独的项目列表,这些项目不一定要包含在结果中,并且要么被删除要么被包括在内遇到F
谓词结果,以及产生false
结果的项目。
我的实施:
我为累加器使用了一对两个列表,其中第一个列表用于确定包含的项目,第二个列表用于那些不包含的项目。
false
示例:
fun <T> List<T>.myDropWhile(predicate: (T) -> Boolean) =
foldRight(Pair(emptyList<T>(), emptyList<T>())) { item, (certain, uncertain) ->
if (predicate(item))
Pair(certain, uncertain + item) else
Pair(certain + uncertain + item, emptyList())
}.first.reversed()
请参阅:runnable demo of this code with more tests。
注意:通过在每次迭代中执行val ints = listOf(0, 0, 0, 1, 0, 2, 3, 0, 0, 4)
println(ints.myDropWhile { it == 0 }) // [1, 0, 2, 3, 0, 0, 4]
或uncertain + item
来复制只读列表会产生certain + uncertain + item
最坏情况时间复杂度,这是不切实际的。使用可变数据结构可以提供O(n^2)
时间。