从给定点列出二维数组对角线元素的最快方法

Fastest way to list diagonal elements of a 2D array from a given point

在二维数组中,对于给定点,在 Scala 中获取对角线元素的最快方法是什么?我知道我可以简单地使用 for 循环从给定点遍历元素,但感觉非常 java-like。我想出的方法之一是使用递归函数,它接受一个函数作为计算下一个单元格的参数。但是我觉得这样的方法效率很低。 Scala 中最惯用的走对角线的方式是什么?

Scala 中的快速函数代码通常涉及尾递归函数,而快速数组访问通常涉及索引。所以你的选择是有限的。

在这种情况下,

def diag(xss: Array[Array[Double]]): Array[Double] = {
  val ans = new Array[Double](xss.length)
  @annotation.tailrec def inner(i: Int) {
    if (i < xss.length) {
      ans(i) = xss(i)(i)
      inner(i+1)
    }
  }
  inner(0)
  ans
}

就个人而言,我发现这不如相应的 while 循环清晰

def diag(xss: Array[Array[Double]]): Array[Double] = {
  val ans = new Array[Double](xss.length)
  var i = 0
  while (i < xss.length) {
    ans(i) = xss(i)(i)
    i += 1
  }
  ans
}

但您的偏好可能会有所不同。

有些优化框架会采用高阶索引遍历(例如for (i <- xss.indices) ans(i) = xss(i)(i))并将其更改为while循环。 (ScalaBlitz 就是其中之一。)

您还可以使用 Tail recursionList 等不可变数据结构,因为函数式编程和不变性非常相配。它提供两个操作 head 和 tail 需要常数时间并且两个操作的时间复杂度是 O(1).

 @annotation.tailrec
  def f(arr: List[List[Int]], res: List[Int] = Nil): List[Int] = {
    if (arr.isEmpty) res
    else 
      f(arr.tail.map(_.tail), res :+ arr.head.head)
  }

  val x = List(List(1, 2, 3, 4), List(5, 6, 7, 8), List(9, 10, 11, 12), List(13, 14, 15, 16))

scala> f(x)
res0: List[Int] = List(1, 6, 11, 16)

只是为了好玩,一些(可以说)更通俗的Scala,注意到对角线元素是由线条的旋转发出的,您可以使用如下代码。不过,它的效率会低于 for 循环。

def diag(m: Array[Array[Int]]) = {
  val (m1, m2) = m.zipWithIndex.
    map{case (l, i) => val (l1, l2) = splitAt(l.size-i); l1 ++ l2}.
    transpose.zipWithIndex.
    map{case (l, i) => l.splitAt(i+1)}.
    unzip
  m1 ++ m2.init
}