您的评论“太多的功能为实时计算可能成为重”,使这个有趣的。基准测试和分析是至关重要的,因为您不想为了性能而编写大量难以维护的代码,只是发现它并不是应用程序中性能至关重要的部分!或者,更糟的是,发现你的性能优化会让你的特定工作负载变得更糟。
性能最佳的实现将取决于您的具体情况(路径有多长?系统上有多少个内核?)但我认为混合使用命令式和功能性方法可能会给您带来最糟糕的两个世界。如果你不小心,你可能会失去可读性和性能!
我会稍微修改missingfaktor's answer以使您从parallel collections获得性能提升。简单地添加.par
可以为您提供巨大的性能提升,这一事实证明了坚持功能性编程的力量!
def distancePar(wps: collection.GenSeq[Waypoint]): Double = {
val parwps = wps.par
parwps.zip(parwps drop 1).map(Function.tupled(distance)).sum
}
我的猜测是,这将工作最好的,如果你有几个核心的问题抛出,并wps
往往是有点长。如果你的内核很少或者路径很短,那么并行性可能会比它所能帮助的更多。
另一个极端将是一个完全必要的解决方案。只要你避免共享的可变状态,写个人的,性能关键的函数的命令性实现通常是可以接受的。但是一旦你习惯了FP,你会发现这种功能更难以编写和维护。并行并不容易。
def distanceImp(wps: collection.GenSeq[Waypoint]): Double = {
if (wps.size <= 1) {
0.0
} else {
var r = 0.0
var here = wps.head
var remaining = wps.tail
while (!remaining.isEmpty) {
r += distance(here, remaining.head)
here = remaining.head
remaining = remaining.tail
}
r
}
}
最后,如果你正在寻找FP和命令之间的中间地带,你可能会尝试递归。我没有介绍它,但我的猜测是,这将大致相当于性能方面的必要解决方案。
def distanceRec(wps: collection.GenSeq[Waypoint]): Double = {
@annotation.tailrec
def helper(acc: Double, here: Waypoint, remaining: collection.GenSeq[Waypoint]): Double =
if (remaining.isEmpty)
acc
else
helper(acc + distance(here, remaining.head), remaining.head, remaining.tail)
if (wps.size <= 1)
0.0
else
helper(0.0, wps.head, wps.tail)
}
回复:您的更新没有任何必要,它只是功能性的,但可能比'zipped.map'解决方案更昂贵。 – missingfaktor 2012-02-17 10:18:58
注意'wps(i)',它可能对某些序列具有'O(n)'复杂性,最终会以'O(n^2)'最终的复杂性结束。 – 2012-02-17 10:21:59
你是对的。选择序列类型时,我应该更加小心。在讨论这个问题时,我已经切换到了可变缓冲区类型,但是我将不得不做一些可能的序列类型和各种建议解决方案的基准。那将是一次很好的体验! – noncom 2012-02-17 10:26:20