Scala:如何打破嵌套的理解

时间:2017-09-04 07:17:19

标签: scala

我试图编写如下代码 -

def kthSmallest(matrix: Array[Array[Int]], k: Int): Int = {
  val pq = new PriorityQueue[Int]() //natural ordering
  var count = 0

  for (
    i <- matrix.indices;
    j <- matrix(0).indices
  ) yield {
      pq += matrix(i)(j)
      count += 1
  } //This would yield Any!

  pq.dequeue() //kth smallest. 

}

我的问题是,我只想循环直到时间计数小于k(类似于takeWhile(count!= k)),但是我还要将元素插入到优先级队列中,这在目前的状态下不会奏效。

我的其他选择是编写嵌套循环并在计数达到k后返回。是否有可能与收益率有关?我找不到一种惯用的做法。任何指针都会有所帮助。

2 个答案:

答案 0 :(得分:1)

有一种方法可以使用Stream 延迟评估来执行此操作。由于for yield等于flatMap,您可以使用for yieldflatMap转换为Stream

matrix.indices.toStream.flatMap(i => {
  matrix(0).indices.toStream.map(j => {
    pq += matrix(i)(j)
    count += 1
  })
}).takeWhile(_ => count <= k)

使用toStream集合转换为Stream,并且由于Stream延迟评估,因此我们可以使用{ {1}} 谓词计数以终止较少的循环而不使用init其他。

答案 1 :(得分:1)

Scala使用var或中断循环并不是惯用的。您可以进行递归,延迟评估或使用胶带break,放弃一些性能(就像return一样,它实现为异常,并且执行效果不佳)。以下是分解的选项:

  1. 使用递归 - 递归算法是函数式语言中循环的模拟

    def kthSmallest(matrix: Array[Array[Int]], k: Int): Int = {
      val pq = new PriorityQueue[Int]() //natural ordering
    
      @tailrec
      def fillQueue(i: Int, j: Int, count: Int): Unit =
        if (count >= k || i >= matrix.length) ()
        else {
          pq += matrix(i)(j)
          fillQueue(
            if (j >= matrix(i).length - 1) i + 1 else i,
            if (j >= matrix(i).length - 1) 0 else j + 1,
            count + 1)
        }
    
      fillQueue(0, 0, 0)
    
      pq.dequeue() //kth smallest.
    }
    
  2. 使用一种懒惰的结构,正如chengpohi建议的那样 - 虽然听起来不像纯粹的功能。在这种情况下,我建议使用Iterator而不是Stream - 因为迭代器不会记住它们经历的步骤(可能为大型矩阵节省一些内存)。

  3. 对于那些非常愿意使用break的人,Scala以可附加的方式支持它(请注意上面提到的性能警告):

    import scala.util.control.Breaks
    
    breakable {
      // loop code
      break
    }