在Kotlin中查找序列的先转换后

时间:2019-02-08 22:22:47

标签: kotlin functional-programming

我经常偶然发现这个问题,但是看不到一个通用的实现:我如何惯用地(从功能上)找到一个元素,在比赛之后停止搜索,还返回一个不同的类型(即,将匹配的任何类型映射到另一种类型) ?

我已经能够解决

fun <F,T> Sequence<F>.mapFirst(block: (F) -> T?): T? =
    fold(AtomicReference<T>()) { ref, from ->
        if (ref.get() != null) return@fold ref
        ref.set(block(from))
        ref
    }.get()

fun main() {
    Files.list(someDir).asSequence().map { it.toFile() }.mapFirst { file ->
        file.useLines { lines ->
            lines.mapFirst { line ->
                if (line == "123") line.toInt() else null
            }
        }
    }?.let { num ->
        println("num is $num") // will print 123 as an Int
    } ?: println("not a single file had a line eq to '123'")
}

但这并不会在比赛中停止(block()返回非null时)会消耗掉所有文件及其所有行。

2 个答案:

答案 0 :(得分:1)

一个简单的for循环足以实现mapFirst

fun <F,T> Sequence<F>.mapFirst(block: (F) -> T?): T? {
    for (e in this) { 
        block(e)?.let { return it }
    }
    return null
}

如果您需要一种解决方案而又不引入自己的扩展名(尽管没有任何问题),则可以使用mapNotNull + firstOrNull组合:

files.asSequence()
    .mapNotNull { /* read the first line and return not null if it's ok */ }
    .firstOrNull()

答案 1 :(得分:0)

我不会映射然后丢弃的值,而是这样做:

sequenceOf(1, 2, 3)
    .firstOrNull() { it == 2 }
    ?.let { it * 2 } ?: 6

首先,找到与您的条件匹配的值,然后根据需要对其进行转换。如果找不到匹配的元素,则分配一个默认值(在本例中为6)。