如何在Kotlin中使用foldRight递归实现dropWhile

时间:2017-09-05 21:50:26

标签: recursion collections kotlin higher-order-functions fold

我一直在使用.foldRight()递归执行更高阶函数,例如anyalltakeWhile,但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() })

1 个答案:

答案 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 dc谓词。< / 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)时间。