在Kotlin中,我如何测试和使用一个值而不计算它两次?



每隔一段时间,我发现自己想为某种过滤操作计算一个值,但是当它已经消失在条件检查中时,又想使用这个值。

例如:

val found = list.firstOrNull { slowConversion(it).isWanted() }
if (found != null) {
something(found, slowConversion(found))
}

when {
other_conditions -> other_actions
list.any { it.contains(regex1) } -> something(list.firstOrNull { it.contains(regex1) } ?: "!!??")
}

对于slowConversion(),我可以使用映射到成对的序列,尽管术语第一和第二有点混淆……

val pair = list.asSequence().map { it to slowConversion(it) }.firstOrNull { it.second.isWanted() }
if ( pair != null ) {
something(pair.first, pair.second)
}

或者如果我只想要转换,

val converted = list.firstNotNullOfOrNull { slowConversion(it).takeIf { it.isWanted() } }

但是我能想到的避免when重复的最好方法是将动作部分移动到条件部分!

fun case(s: List<String>, r: Regex) {
val match = s.firstOrNull { it.contains(r) }?.also { something(it) }
return match != null
}
when {
other_conditions -> other_actions
case(list, regex1) -> true
}
在这一点上,似乎我应该有一个函数调用的堆栈链接在一起||
other_things || case(list, regex1) || case(list, regex2) || catchAll(list)

是否有更好或更简洁的东西?

你可以这样写你的第一个例子:

for(element in list) {
val result = slowConversion(element)
if(result.isWanted()) {
something(element, result)
break
}
}

这可能看起来不太像kotlin,但我认为这是相当直接的&很容易理解

对于第二个示例,可以使用find函数:
when {
other_conditions -> other_actions
else -> list.find { it.contains(regex1) }?.let(::something)
}

如果您有多个正则表达式,只需遍历它们,

val regexes = listOf(regex1, regex2, ...)
for(regex in regexes) {
val element = list.find { it.contains(regex1) } ?: continue
something(element)
break
}

最新更新