加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala正确定义抽象数据类型的空值

发布时间:2020-12-16 18:06:33 所属栏目:安全 来源:网络整理
导读:我有一个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] 当我尝试构建一个随机创建树的函数时,我遇到了E
我有一个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&lt ;: Y那么树[X]&lt ;:树[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]上是逆变的,因此您无法使节点协变.此时你可能会想:

Who cares about Node? I just want Tree to be covariant!

好吧,通过使其超类型树协变节点在实践中变得如此. 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 {})

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读