Kotlin 函数式向量点积

Kotlin Functional Vector Dot Product

我有一个 Kotlin Vector class,它包含一个函数来计算两个 Vector 的点积:

class Vector(val values: Array<Double>) {

    fun dot(v: Vector): Double {
        require(this.values.size == v.values.size)
        var product = 0.0
        for (i in this.values.indices) {
            product += this.values[i] * v.values[i]
        }
        return product
    }
}

我想用函数式的方式表达两个向量的点积。折叠会初始化,但我不知道如何让它与两个数组一起工作。

有人有什么建议吗?

您可以使用转换函数压缩它们,然后求和:

return values.asSequence().zip(v.values.asSequence()) { a, b -> a * b }.sum()

return values.zip(v.values) { a, b -> a * b }.sum()

return values.zip(v.values, Double::times).sum()

我会 zip the the two arrays, and then use sumByDouble:

fun dot(v: Vector): Double = values
    .apply { require(size == v.values.size) }
    .zip(v.values)
    .sumByDouble { (a, b) -> a * b }

旁注:如果您使用的是 JVM,则可以 。前者在 JVM 上将表示为一个 double[],而不是一个装箱的 Double 数组。

接受的答案是准确的,但效率不高:将数组压缩在一起会分配一个新列表,复制两个源数组,并为每个数据元素生成一个 Pair,所有这些都会很快处理掉后。这会使内存消耗增加一倍以上。

我不认为你上面的代码真的那么糟糕,尽管我可能会将它实现为 DoubleArray 上的扩展函数:

infix fun DoubleArray.dot(other: DoubleArray): Double {
  var out = 0.0
  for (i in 0 until size) out += this[i] * other[i]
  return out
}

这纯粹是功能性的,因为它没有副作用,并且总是 returns 给定相同的输入得到相同的结果。您的消费者永远不会知道您的 for 循环秘密。

可以 使用 foldIndexed 来完成,但是,对于那种光滑、实用的 Kotlin 样式:

infix fun DoubleArray.dot(other: DoubleArray) =
   foldIndexed(0.0) { i, acc, cur -> acc + cur * other[i] }

虽然我觉得这有点难读(所有那些 curs 和 accs,加上一个奇怪的浮动 0.0);在幕后,foldIndexed 只是在使用 for 循环。