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)
}
当然这不行。
- 语法不正确。我需要将节点添加到列表中并调用它,因为它是 parent
- 递归调用不起作用,因为它需要 Node 但得到 Future[Option[Node]],也不知道如何解决这个问题:(
我记得几年前做过 Haskell,我的功能风格有点(非常)生疏。
编辑:我发现我可能还需要将列表作为参数...哎呀,我被困住了 -.-
首先,您不需要将列表作为参数包含在内,但您需要使用嵌套函数来执行此操作。
这里的主要问题是 find
return 是 Future[Option[Node]]
,所以这意味着您需要完全 "inside" 期货。 Future
是一个 monad,它基本上意味着两件事:
- 您永远无法直接访问
Future
中的值
- 您通过
map
和 flatMap
将函数传递给它来与包含的值进行交互,这会在 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
。
尝试将旧的 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)
}
当然这不行。
- 语法不正确。我需要将节点添加到列表中并调用它,因为它是 parent
- 递归调用不起作用,因为它需要 Node 但得到 Future[Option[Node]],也不知道如何解决这个问题:(
我记得几年前做过 Haskell,我的功能风格有点(非常)生疏。
编辑:我发现我可能还需要将列表作为参数...哎呀,我被困住了 -.-
首先,您不需要将列表作为参数包含在内,但您需要使用嵌套函数来执行此操作。
这里的主要问题是 find
return 是 Future[Option[Node]]
,所以这意味着您需要完全 "inside" 期货。 Future
是一个 monad,它基本上意味着两件事:
- 您永远无法直接访问
Future
中的值
- 您通过
map
和flatMap
将函数传递给它来与包含的值进行交互,这会在 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
。