递归代数数据类型和惰性评估的有用性的典型示例是游戏算法,例如,如John Hughes着名的WhyFP论文所示( Comp.J.,Vol.32,No。2,1989 )。
使用Scala实现它,并对游戏的每个子树使用延迟评估的Stream[Tree[A]]
,导致trait Tree[A]
定义:
sealed trait Tree[A]
case class Branch[A](ts: Stream[Tree[A]]) extends Tree[A]
case class Leaf[A](a: A) extends Tree[A]
现在,一个懒惰评估,可能是无限的游戏可以表示为:
gameTree(b: Board): Tree[Board] =
if (b.isAtEndPos) Leaf(b)
else Branch(b.emptySlots.toStream.map((pos: Int) => gameTree(b addTic pos)))
您可以实施修剪,评分和并行化策略 实际的算法,例如 minimax 来完成这项工作,以及 评估树的必要部分:
def maximize(tree: Tree[Board]): Int = tree match {
case Leaf(board) => board.score
case Branch(subgames) =>
subgames.map((tree: Tree[Board]) => minimize(tree)).max
} ...
def minimize // symmetrically
然而,无限流引入了显着的性能损失,使用急切列表(ts: List[Tree[A]]
)解决相同的游戏树的效率提高了25倍。
在类似的情况下,有没有办法在Scala中有效地使用Streams或lazy结构?
编辑:添加了一些性能结果和实际代码: 在link中是懒惰版本。
懒惰版(scala 2.9.1):
Time for gametree creation: 0.031s and for finding the solution 133.216s.
树创建中没有转换,即在 minimax 中的集合上进行映射
Time for gametree creation: 4.791s and for finding the solution 6.088s.
在gameTree创建中转换为列表
Time for gametree creation: 4.438s and for finding the solution 5.601s.
答案 0 :(得分:4)
如果您希望快速而粗略地了解时间的变化,可以尝试运行:
JAVA_OPTS="-Xprof" scala TicTacToe
正如评论中所述,我无法重现您的体验。