web-dev-qa-db-ja.com

空のKotlin配列でreduceを呼び出す方法は?

空の配列を単純に削減するとスローされます:

スレッド「メイン」の例外Java.lang.UnsupportedOperationException:空の反復可能オブジェクトを減らすことはできません。

チェーン時の同じ例外:

val a = intArrayOf()

val b = a.reduce({ memo, next -> memo + next }) // -> throws an exception

val a1 = intArrayOf(1, 2, 3)

val b1 = a.filter({ a -> a < 0 }).reduce({ a, b -> a + b }) // -> throws an exception

それは削減の予想される操作ですか、それともバグですか?

回避策はありますか?

35
dippe

例外は正しく、reduceは空のイテラブルまたは配列では機能しません。おそらく探しているのはfoldです。これは、開始値と、反復可能オブジェクトの各要素に連続して適用される操作を受け取ります。 reducefirst要素を開始値として取るため、引数として追加の値を渡す必要はありませんが、コレクションが空でないことが必要です。

foldの使用例:

println(intArrayOf().fold(0) { a, b -> a + b })  // prints "0"
61

fold(...)を使用できない状況のためのより一般的なアプローチを追加したいだけです。なぜなら、foldを使用するには、いくつかの初期値を表現できる必要があるからです。

someIterable
    .filter{ TODO("possibly filter-out everything") }
    .takeIf{ it.isNotEmpty() }
    ?.reduce{ acc, element -> TODO("merge operation") }
    ?: TODO("value or exception for empty")

このアプローチでは、空のコレクションの場合、reducetakeIfに変換するため、nullは実行されません。そして最後に、elvis演算子を使用して、その場合に何らかの値を表す(または例外をスローする)ことができます。

あなたの例:

intArrayOf(1, 2, 3)
    .filter({ a -> a < 0 })
    .takeIf{ it.isNotEmpty() }
    ?.reduce({ a, b -> a + b })
    ?: 0
2
Tomac Antonio
public inline fun <S, T : S> List<T>.reduceRightDefault(defaultIfEmpty: S, operation: (T, acc: S) -> S): S {
    return if (isEmpty()) defaultIfEmpty
    else reduceRight(operation)
}

使用法:

val result = listOf<Boolean>().reduceRightDefault(false) { first, second -> first && second}

println("result $result")//result false
0
NickUnuchek

あなたはfoldRightを使うことができます:

println(listOf("1", "2", "3")
    .filter { "not found" == it }
    .foldRight("") { a, b -> a + b }) // prints: ""

println(listOf("1", "2", "3")
    .filter { "not found" != it }
    .foldRight("") { a, b -> a + b }) // prints: "123"

またはあなたの場合:

val a = intArrayOf()
val b = a.foldRight(0) { memo, next -> memo + next } // b == 0
val a1 = intArrayOf(1, 2, 3)
val b1 = a.filter { a -> a < 0 }.foldRight(0) { a, b -> a + b } // b1 == 0
0