2016-07-27 64 views
0

我有类似:链接更高的链接类型,可能吗?

trait Node[P <: Node[_]] 

class RootNode extends Node[Null] { 
    val refB : NodeB[RootNode] = .... 
} 

class NodeB[P <: Node[_]] extends Node[P] { 
    val refC : NodeC[NodeB[P]] = .... 
} 

class NodeC[P <: Node[_]] extends Node[P] { 
    val refD : NodeD[NodeC[P]] = .... 
} 

有没有更好的方式来处理这种类型的结构?不知何故,用我的方法,我们可以使用P来限制直接的父级,但是我们已经失去了父级的父级(依此类推),因此限制不会很紧。如果不想松散所有的上下文,我将不得不将其改变成类似于:

class NodeC[P <: Node[_]] extends Node[P] { 
    val refD : NodeD[NodeC[NodeB[NodeA[RootNode]]] = .... 
} 

这是完全不可行的。

我试过的任何方法都导致我导致非法循环引用。有没有解决这个问题的方法?

回答

1

您是否考虑过将限制代表为不同类型的结构?这种嵌套确实是可能的,但它看起来像你可以使用HList执行的那种关注。

而不是以这种方式表示refs,为什么不简单地通过使用类似下面的内容来实现Node。我在这里提供了一些通用示例,说明如何使用超级简单的不成形模式进行操作。

如果你可以更具体地了解需求,我相信很多人在这里可以做更多的帮助,我的直觉告诉我有一个更简单的方法围绕HList可以回答你的问题,没有任何讨厌的类型杂耍。

import shapeless._ 
import shapeless.ops.hlist._ 
import shapeless.:: 

class Node[P <: Hlist](hl: P) { 
    def append[T](obj: T): Node[P :: T] = new Node[P :: T](hl :: obj) 

    // Much like a normal List, HList will prepend by default. 
    // Meaning you need to reverse to get the input order. 
    def reverse[Out]()(
    implicit rev: Reverse.Aux[P, Out] 
    ): Out = rev(hl) 

    // you can enforce type restrictions with equality evidence. 
    // For instance you can use this to build a chain 
    // and then make sure the input type matches the user input type. 
    def equalsFancy[V1 <: Product, Rev, Out <: Product](v1: V1)(
    // We inverse the type of the HList to destructure it 
    // and get the initial order. 
    implicit rev: Reverse.Aux[P, Rev], 
    // then convert it to a tuple for example. 
    tp: Tupler.Aux[Rev, Out], 
    ev: V1 =:= Out 
    ): Boolean = tp(hl) == v1 
} 
object Node { 
    def apply: Node[HNil] = new Node[HNil] 

    Node().append[String]("test").append[Int](5).equalsFancy("test" -> 5) 
} 

这是很容易通过使用LUBConstraint以及限制要在列表中的Node只亚型类型元素。(下上限的类型)。

class NodeList[HL <: HList](list: Node[_] :: HL)(implicit val c: LUBConstraint[HL, Node[_]) 

这意味着你不能再追加不在_ <:< Node[_]NodeList这可能给你一些细微Poly元素。

trait A 
trait B 
object printPoly extends Poly1 { 
    // Let's assume these are your A, B and Cs 
    // You can use Poly to define type specific behaviour. 
    implicit def caseNodeA[N <: Node[A]] = at[N](node => println("This is an A node")) 
    implicit def caseNodeB[N <: Node[B]] = at[N](node => println("This is a B node")) 
implicit def unknown[N <: Node[_]] = at[N](node => println("This is not known to us yet")) 
} 
val nodeList: NodeList[..] = .. 
nodeList.list.map(printPoly) 

更新

这是值得推行的树形结构即可。

case class Node[A, F <: HList](value: A, children: F) { 
    def addChild[T, FF <: HList](
     child: Node[T, FF] 
    ): Node[A, HTree[T, FF] :: F] = { 
     new Node(value, child :: children) 
    } 

    def values = Node.Values(this) 
    } 

    object Node { 
    def apply[A](label: A) = new Node[A, HNil](label, HNil) 

    object Values extends Poly1 { 
     implicit def caseHTree[A, F <: HList, M <: HList](
     implicit fm: FlatMapper.Aux[getLabels.type, F, M], 
      prepend: Prepend[A :: HNil, M] 
     ): Case.Aux[HTree[A, F], prepend.Out] = 
      at[HTree[A, F]](tree => prepend(
      tree.value :: HNil, 
      fm(tree.children)) 
     ) 
    } 
    } 
+0

感谢您的建议。我已经想过做一个类似于一个包装器的东西,不像一个List,而是像一棵树(最后同样的东西),其中每个Node都是其他Tree条目的容器......但是然后你需要展开为了访问元素本身,并使用它听起来有点复杂。另外,我不得不承认,达到我父亲最初在我的帖子中所建议的水平对于需求来说是非常好的,但不是100%理想的,只是为了知道我希望找到一个更好的解决方案,而不需要任何进一步的包装。 – lqbweb