在不断增长的scala.collections.mutable.Queue上有一种优雅的foldLeft方式吗?

时间:2013-03-29 13:45:36

标签: scala functional-programming queue mutable

我有一个递归函数,我试图通过让内部递归部分(@tailrec)向队列中添加元素countR3 agenda scala.collections.mutable.Queue })。我的想法是将函数的外部部分放在议程上并总结结果。

注意:这个 是一个家庭作业问题,因此我不想发布整个代码;然而,使实现尾递归是的功课的一部分。

以下是与我的问题相关的代码部分:

import scala.collection.mutable.Queue

val agenda: Queue[Tuple2[Int, List[Int]]] = Queue()

@tailrec
def countR3(y: Int, x: List[Int]): Int = {
  if (y == 0) 1
  else if (x.isEmpty) 0
  else if …
  else {
    agenda.enqueue((y - x.head, x))
    countR3(y, x.tail)
  }
}
⋮
agenda.enqueue((4, List(1, 2)))
val count = agenda.foldLeft(0) {
  (count, pair) => {
    val mohr = countR3(pair._1, pair._2)
    println("count=" + count + " countR3=" + mohr)
    count + mohr
  }
}
println(agenda.mkString(" + "))
count

这个几乎似乎有效...问题是它不会迭代添加到议程中的所有项目,但它会处理一些。您可以在下面的输出中看到:

count=0 countR3=0
count=0 countR3=0
count=0 countR3=0
(4,List(1, 2)) + (3,List(1, 2)) + (2,List(2)) + (2,List(1, 2)) + (1,List(2)) + (0,List(2))

[在最终议程中的六个项目中,只处理了前三个项目。]

我一般都很清楚在你用Java编写迭代时改变集合的危险。但是队列是一种不同颜色的马。当然,我理解我可以简单地写一个命令式循环,如下所示:

var count = 0
while (!agenda.isEmpty) {
  val pair = agenda.dequeue()
  count += countR3(pair._1, pair._2)
}

这非常有效,但这是Scala,我正在探索是否有更多功能优雅的方式。

有什么建议吗?

1 个答案:

答案 0 :(得分:2)

虽然可能不完全惯用,但你可以试试这个:

Stream.continually({ if (agenda.isEmpty) None else Some(agenda.dequeue()) }).
    takeWhile(_.isDefined).flatten.
    map({ case (x, y) => countR3(x, y) }).
    toList.sum
  • Stream.continually({ if (agenda.isEmpty) None else Some(agenda.dequeue()) })为您提供了包含在Option[Tuple2[Int, List[Int]]]中的无限队列项。
  • 然后,takeWhile(_.isDefined)会在遇到第一个None项目时立即切断序列,即队列耗尽时。
  • 由于之前的调用仍会产生一系列Option s,我们需要使用flatten打开它们。
  • map({ case (x, y) => countR3(x, y) })基本上将您的功能应用于每个项目。
  • 最后,toList强制对流进行评估(这就是我们正在使用的内容),然后sum计算列表项的总和。

我认为agenda.foldLeft的问题(它只处理'某些'排队的项目)是我猜它需要一个(可能是不可变的)当前入队项目列表,因此不会受到影响在计算过程中添加的项目。

相关问题