Scala Slick 3 游戏框架

Scala Slick 3 Playframework

尝试将旧的 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
}

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

private def getAllParents(node: Node): List[Node] = node.parentID match
{
  case l:Long => List(node) + getAllParents(find(l))
  case None => List(node)
}

当然这不行。

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

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

编辑:我发现我可能还需要将列表作为参数...哎呀,我被困住了 -.-

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

这里的主要问题是 find return 是 Future[Option[Node]],所以这意味着您需要完全 "inside" 期货。 Future 是一个 monad,它基本上意味着两件事:

  1. 您永远无法直接访问 Future
  2. 中的值
  3. 您通过 mapflatMap 将函数传递给它来与包含的值进行交互,这会在 return.[=59= 中为您提供一个新的 Future ]

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

val x: Future[Int] = Future { 5 }

如果我想将包含的值加 1,我不能直接访问它,而是可以调用 map:

val y: Future[Int] = x.map{i => i + 1}

同样,如果我有一些功能,例如:

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

我可以像这样在未来使用它:

val y: Future[Int] = x.flatMap{i => addOne(i)}

回到你的情况,这意味着 getAllParents 必须 return 一个 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).flatMap{parentOption => 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]]

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