【问题标题】:How to call reduce on an empty Kotlin array?如何在空的 Kotlin 数组上调用 reduce?
【发布时间】:2016-06-10 04:58:27
【问题描述】:

空数组上的简单reduce会抛出:

线程“main”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

是reduce的预期操作还是一个bug?

有什么解决方法吗?

【问题讨论】:

  • 仅供参考:如果您只是使用reduce 进行求和,那么您可以在数字列表中使用内置的.sum(),如果您要对一些特殊的东西求和,那么您可以编写一个扩展功能也可以提供帮助。

标签: reduce kotlin


【解决方案1】:

例外是正确的,reduce 不适用于空的可迭代对象或数组。您可能正在寻找的是fold,它采用一个起始值和一个操作,该操作连续应用于可迭代的每个元素。 reduce 将 first 元素作为起始值,因此不需要额外的值作为参数传递,但要求集合不为空。

fold的用法示例:

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

【讨论】:

  • 谢谢,折叠似乎是解决方案 :) 所以我可以理解链式过滤和减少是有风险的,因为过滤器可以返回一个空列表,并且总是会调用 reduce,所以会抛出异常.
  • 你为什么不定义 reduce 类似于它在 ECMAScript 6 中的定义?让reduce 接受两个参数,函数和初始值。如果没有定义初始值(又名null),reduce 假定数组中的第一个值作为初始值。
  • 他们叫它reduce吗?如果是这样,寻找fold 的人可能没有意识到当他们想要fold 时需要reduce。如果他们还提供fold,那么他们已经创建了两种方法来做同样的事情。无论哪种方式,这似乎都会给程序员带来额外的障碍。我认为这不适合这种语言;但是,如果我们发现我们需要/想要那种简写,我们仍然可以通过创建自己的函数来实现同样的目的。
【解决方案2】:

我只想为无法使用fold(...) 的情况添加更通用的方法。因为,为了使用fold,您需要能够表达一些初始值。

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

使用这种方法,如果集合为空,reduce 将不会被执行,因为takeIf 会将其转换为null。最后我们可以在这种情况下使用 elvis 运算符来表达一些值(或抛出异常)。

你的例子:

intArrayOf(1, 2, 3)
    .filter { a -> a < 0 }
    .takeIf { it.isNotEmpty() }
    ?.reduce { a, b -> a + b }
    ?: 0

编辑:

从 kotlin 1.4 开始,stdlib 函数reduceOrNull 比.takeIf { it.isNotEmpty() } 方法更合适。

一般情况:

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

用于您的问题:

intArrayOf(1, 2, 3)
    .filter { a -> a < 0 }
    .reduceOrNull { a, b -> a + b }
    ?: 0

【讨论】:

  • 如何在使用reduce时添加断点。?在字符串上。?
  • 您可以简单地在 lambda 主体上中断,它会在每个 reduce 子步骤中中断,允许您检查 a 和 b
  • 感谢回复我所做的是我使用了 take(numberOfItem) 并且它有效。
【解决方案3】:
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

【讨论】:

    【解决方案4】:

    你可以使用 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-14
      • 1970-01-01
      • 2021-11-25
      • 2022-11-09
      • 2020-04-24
      • 2021-04-25
      • 2018-08-16
      • 2021-01-14
      相关资源
      最近更新 更多