scala正确定义抽象数据类型的空值
我有一个ADT如下:
sealed trait Tree[A] case object EmptyTree extends Tree[Nothing] case class Leaf[A](value: A) extends Tree[A] case class Node[A](op: Seq[A] => A,branches: Tree[A]*) extends Tree[A] 当我尝试构建一个随机创建树的函数时,我遇到了EmptyTree的问题,类型系统不允许通过 def create(acc: Tree[A],currentDepth: Int): Tree[A] = currentDepth match { case maxDepth => Leaf(terminalSet(r.nextInt(terminalSet.length))) case 0 => { val op_pos = r.nextInt(fSetLength) val branches: Seq[Tree[A]] = for (i <- 0 to r.nextInt(fSetLength)) yield create(EmptyTree,currentDepth+1) Node(functionSet(op_pos)._1,branches:_*) } case _ => { if (r.nextFloat() <= probF) { val op_pos = r.nextInt(fSetLength) val branches = for (i <- 0 to r.nextInt(fSetLength)) yield create(EmptyTree,currentDepth + 1) Node(functionSet(op_pos)._1,branches:_*) } else Leaf(terminalSet(r.nextInt(terminalSet.length))) } } create(EmptyTree,0) 基本上在create(EmptyTree,currentDepth 1)中它抱怨它正在期待一个Tree [A]并且正在接收一个EmptyTree.type 解决方法
编译器异议是合理的.编译器需要Tree [A]并且您正在传递EmptyTree,其超类型是Tree [Nothing].先验地,这两种类型之间没有子类型关系.
你想要的是树是协变的:如果X< ;: Y那么树[X]< ;:树[Y].然后,对于任何A,Nothing<:A,你得到EmptyTree.type<:Tree [A],你总是可以在需要树[A]时传递EmptyTree. 在树协变中声明A参数的语法是Tree [A];改变它,你的代码应该编译. 这是关于Scala:Be friend with covariance and contravariance中协方差和逆变的一篇好文章 更新在questioning answer之后,我实际上已经查看了Tree的构造函数,并且根据定义,您无法使Tree协变.可悲的是,编译器不会抱怨(你看,它实际上应该抱怨更多).您在Node中的操作在Seq [A]上是逆变的,因此您无法使节点协变.此时你可能会想:
好吧,通过使其超类型树协变节点在实践中变得如此. scalac实际上应该检查协变者的所有子类型构造函数是否(或可能是)协变的.无论如何,代码显示如下: // you need a value for EmptyTree! thus default def evaluateTree[Z](tree: Tree[Z],default: Z): Z = tree match { case EmptyTree => default case Leaf(value) => value // note how you need to essentially cast here case Node(op: (Seq[Z] => Z),args @ _*) => op(args map { branches => evaluateTree(branches,default) }) } trait A trait B extends A val notNice: Tree[A] = Node[B]({ bs: Seq[B] => bs.head },EmptyTree) // ClassCastException! val uhoh = evaluateTree(notNice,new A {}) 更新2回到原来的问题:)我会让你的树类型不变,并有一个EmptyTree [A]()案例类;遗憾的是没有无参数值类. sealed trait Tree[A] case class EmptyTree[A]() extends Tree[A] case class Leaf[A](value: A) extends Tree[A] // I wouldn't use varargs here,make a method for that if you want case class Node[A](op: Seq[A] => A,branches: Tree[A]*) extends Tree[A] // for convenience,it could be inside `Tree` companion def emptyTree[A]: EmptyTree[A] = EmptyTree() def evaluateTree[Z](tree: Tree[Z],default: Z): Z = tree match { case EmptyTree() => default case Leaf(value) => value // no need to match generic types or anything here case Node(op,default) }) } trait A trait B extends A // doesn't work now // val notNice: Tree[A] = Node[B]({ bs: Seq[B] => bs.head },emptyTree) val notNice: Tree[B] = Node[B]({ bs: Seq[B] => bs.head },emptyTree) // doesn't compile,no class cast exception // val uhoh = evaluateTree(notNice,new A {}) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |