Scala Slick 3 游戏框架

Posted

技术标签:

【中文标题】Scala Slick 3 游戏框架【英文标题】:Scala Slick 3 Playframework 【发布时间】:2015-05-13 21:54:41 【问题描述】:

尝试将旧的 php 应用程序移植到 scala 有点卡住,我想要什么: 我有如下所示的节点(模型):

id | parent_id

现在对于给定的节点

case class Node(id: Option[Long], parentID: Option[Long])

private def filterQuery(id: Long): Query[Nodes, Node, Seq] = nodes.filter(_.id === id)

override def find(id: Long): Future[Option[Node]] =

    try db.run(filterQuery(id).result.headOption)
    finally db.close

我想递归地得到所有的父母,比如:

private def getAllParents(node: Node): List[Node] = node.parentID match

  case l:Long => List(node) + getAllParents(find(l))
  case None => List(node)

这当然行不通。

    语法不正确。我需要将节点添加到列表中并为其父级调用它 递归调用不起作用,因为它需要 Node 但得到 Future[Option[Node]],也不知道如何解决这个问题:(

我记得几年前做过 Haskell,我的功能风格有点(非常)生疏。

编辑:我发现我可能还需要将列表作为参数...啊,我卡住了-.-

【问题讨论】:

【参考方案1】:

首先,您不需要将列表作为参数包含在内,但您需要使用嵌套函数来执行此操作。

这里的主要问题是find 返回一个Future[Option[Node]],因此这意味着您需要完全在“内部”期货中工作。 Future 是一个 monad,基本上意味着两件事:

    您永远不能直接访问Future 中的值 您可以通过mapflatMap 将函数传递给包含的值,从而与包含的值进行交互,这会为您提供一个新的Future 作为回报。

我建议您阅读一些有关基于未来的编程如何工作的文章,here's 我找到了一篇不错的文章,但作为一个简化的示例,假设我有:

val x: Future[Int] = Future  5 

如果我想给包含的值加1,我不能直接访问它,但我可以调用map

val y: Future[Int] = x.mapi => i + 1

同样,如果我有类似的功能:

def addOne(i: Int): Future[Int] = //...

我可以将它用于我的未来,例如:

val y: Future[Int] = x.flatMapi => addOne(i)

回到你的情况,这意味着getAllParents 必须返回一个Future[List[Node]],我们将使用flatMap 进行递归,每次调用 find 时你都会创建一个新的未来,并在遍历时将它们链接在一起通过他们。

private def getAllParents(node: Node): Future[List[Node]] = 
  def loop(node: Node, build: List[Node]): Future[List[Node]] = 
    node.parentId match 
      case None => Future.successful(node :: build)
      case Some(parentId) => find(parentId).flatMapparentOption => parentOption match 
        case Some(parentNode) => loop(parentNode, node :: build)
        case None => throw new Exception(s"couldn't find parent for $node")
      
    
  
  loop(node, Nil)

所以你可以看到loop 是一种递归函数,但递归是在Future 内完成的。如果节点没有父节点,那么我们基本上将最终结果直接粘贴到新的Future 中,然后就结束了。如果节点确实有父节点,则我们调用find,这给了我们一个Future[Option[Node]]

现在进行递归,我们在这个返回的Future 上调用flatMap,我们必须给它一个Option[Node] => Future[List[Node]] 类型的函数。现在我们有了一个可以同时访问node 及其父级的函数,我们可以将node 添加到列表中,并在其父级上再次调用loop

【讨论】:

谢谢你,你说得对,我对 Futures 还不是很满意。我确实需要“递归找到的父母”在另一个表中建立一棵树(它是一个闭包表实现),而期货“只是”一个有时会有一些东西的合同。 (如果我理解正确的话)所以这是一种“不,我想要尽快!” :X 还有一个问题:我已经有一个 TableQuery: private val nodes = TableQuery[Nodes] and private def all(): Query[Nodes, Node, Seq] = nodes 会使用它们而不是 find not大大提高了性能(只需一次而不是多次)。我该如何过滤这些?

以上是关于Scala Slick 3 游戏框架的主要内容,如果未能解决你的问题,请参考以下文章

干净的 sbt 项目中的 Scala Play 框架依赖项

Play 框架作为游戏服务器

Scala + Play Framework + Slick - Json 作为模型字段

Scala Play Framework Slick 会话不起作用

jooq无法在游戏中生成类 - scala

如何使用 Play with Scala 和 Slick 从数据库中获取记录