我有一个递归函数,我试图通过让内部递归部分(@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,我正在探索是否有更多功能优雅的方式。
有什么建议吗?
答案 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
的问题(它只处理'某些'排队的项目)是我猜它需要一个(可能是不可变的)当前入队项目列表,因此不会受到影响在计算过程中添加的项目。