我目前有一个方法,使用scala.collection.mutable.PriorityQueue以一定的顺序组合元素。例如,代码看起来有点像这样:
def process[A : Ordering](as: Set[A], f: (A, A) => A): A = {
val queue = new scala.collection.mutable.PriorityQueue[A]() ++ as
while (queue.size > 1) {
val a1 = queue.dequeue
val a2 = queue.dequeue
queue.enqueue(f(a1, a2))
}
queue.dequeue
}
代码按照编写的方式工作,但必须是非常命令式的。我想使用SortedSet而不是PriorityQueue,但是我的尝试使这个过程看起来混乱了很多。做我想做的事情有什么更明确、更简洁的方式吗?
如果f不产生已经存在于集合中的元素,则确实可以使用SortedSet
。(如果是这样,则需要一个不可变的优先级队列。)一种声明性的方法是:
def process[A:Ordering](s:SortedSet[A], f:(A,A)=>A):A = {
if (s.size == 1) s.head else {
val fst::snd::Nil = s.take(2).toList
val newSet = s - fst - snd + f(fst, snd)
process(newSet, f)
}
}
试图改进@Kim Stebel的答案,但我认为祈使式变体仍然更清楚。
def process[A:Ordering](s: Set[A], f: (A, A) => A): A = {
val ord = implicitly[Ordering[A]]
@tailrec
def loop(lst: List[A]): A = lst match {
case result :: Nil => result
case fst :: snd :: rest =>
val insert = f(fst, snd)
val (more, less) = rest.span(ord.gt(_, insert))
loop(more ::: insert :: less)
}
loop(s.toList.sorted(ord.reverse))
}
这是SortedSet
和Stream
的解决方案:
def process[A : Ordering](as: Set[A], f: (A, A) => A): A = {
Stream.iterate(SortedSet.empty ++ as)( ss =>
ss.drop(2) + f(ss.head, ss.tail.head))
.takeWhile(_.size > 1).last.head
}