我试图编写如下代码 -
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后返回。是否有可能与收益率有关?我找不到一种惯用的做法。任何指针都会有所帮助。
答案 0 :(得分:1)
有一种方法可以使用Stream
延迟评估来执行此操作。由于for yield
等于flatMap
,您可以使用for yield
将flatMap
转换为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
一样,它实现为异常,并且执行效果不佳)。以下是分解的选项:
使用递归 - 递归算法是函数式语言中循环的模拟
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.
}
使用一种懒惰的结构,正如chengpohi建议的那样 - 虽然听起来不像纯粹的功能。在这种情况下,我建议使用Iterator
而不是Stream
- 因为迭代器不会记住它们经历的步骤(可能为大型矩阵节省一些内存)。
对于那些非常愿意使用break
的人,Scala以可附加的方式支持它(请注意上面提到的性能警告):
import scala.util.control.Breaks
breakable {
// loop code
break
}