【问题标题】:Short circuiting operation in Kotlin sequencesKotlin 序列中的短路操作
【发布时间】:2019-07-25 20:08:17
【问题描述】:

我一直想知道是否有机会使用map 运算符执行一些操作n 次的序列,但可以在“开始”处理期间终止该执行?这是我尝试使用 Kotlin 序列实现的命令式代码:

val offers = mutableListOf<String>()
for (pageNumber in FIRST_PAGE_NUMBER until numberOfPages) {
     val offersInPage = findByPage(query, pageSize, pageNumber)
     offers.addAll(offersInPage)

     if(offersInPage.size == 5)
         break
}

主要的是,当前一个响应满足某些条件时,我想挂起处理对外部服务的任何进一步请求。

当尝试以更具声明性的方式实现它时,我最终得到了这样的结果:

IntArray(numberOfPages)
    .asSequence()
    .map { findByPage(query, pageSize, it) }
    .takeWhile { it.size == 5 }
    .flatten()
    .toList()

但是findByQuery方法被调用n次,然后结果被过滤。是否有任何运算符可以帮助我实现诸如在满足给定条件后终止该惰性操作之类的操作?

【问题讨论】:

    标签: kotlin functional-programming sequence declarative


    【解决方案1】:

    该序列完全按照您的预期工作。它在 map 上执行 takeWhile 条件检查,并且只有在它为 true 时才会继续。

    一个问题可能是IntArray(numberOfPages)。这将创建一个大小为numberOfPages0 数组。所以你遍历0 的序列而不是页码。您可以简单地将其更改为 for 循环条件。

    (FIRST_PAGE_NUMBER until numberOfPages)
        .asSequence()
    

    另一个问题可能是takeWhile。在 for 循环中,您在 第一个带有size == 5 的元素之后停止。但是在序列中,您在 之前 使用size != 5 停止第一个元素。这个问题最简单的解决方案是在offers.addAll(offersInPage) 被执行之前找到另一个会中断循环的条件。如果这是不可能的,你可以使用这样的东西:

    fun <T> Sequence<T>.takeWhileEndInclusive(predicate: (T) -> Boolean) = object : Sequence<T> {
        val sequence = this@takeWhileEndInclusive
    
        override fun iterator() = object : Iterator<T> {
            val iterator = sequence.iterator()
            var nextState: Int = -1
            var nextItem: T? = null
            var found = false
    
            private fun calcNext() {
                if (!found && iterator.hasNext()) {
                    val item = iterator.next()
                    if (!predicate(item)) {
                        found = true
                    }
                    nextState = 1
                    nextItem = item
                    return
                }
                nextState = 0
            }
    
            override fun next(): T {
                if (nextState == -1)
                    calcNext()
                if (nextState == 0)
                    throw NoSuchElementException()
                @Suppress("UNCHECKED_CAST")
                val result = nextItem as T
    
                nextItem = null
                nextState = -1
                return result
            }
    
            override fun hasNext(): Boolean {
                if (nextState == -1)
                    calcNext()
                return nextState == 1
            }
        }
    }
    

    这是默认takeWhile 实现的略微调整版本。

    用法:

    (FIRST_PAGE_NUMBER until numberOfPages)
        .asSequence()
        .map { findByPage(query, pageSize, it) }
        .takeWhileEndInclusive { it.size != 5 }
        .flatten()
        .toList()
    

    【讨论】:

      猜你喜欢
      • 2011-07-16
      • 2015-12-06
      • 2014-06-08
      • 2019-01-05
      • 1970-01-01
      • 2013-03-11
      • 2012-10-06
      • 2020-05-27
      • 2016-08-21
      相关资源
      最近更新 更多