尝试创建一个泛型函数来对树的元素求和

时间:2015-12-26 20:09:54

标签: scala generics

我正在使用此处定义的树...

https://gist.github.com/schmmd/1271891

其中...

class Tree[+T]
case object Empty extends Tree[Nothing] 
case class Node[T](val elem: T, val left: Tree[T], val right: Tree[T]) extends Tree[T]

我添加了这个特性...

trait sum[T] {
  def +(value: T): T
}

我定义了这棵树......

val a = Node(1, Node(1, Empty, Node(5, Empty, Empty)), Node(2, Node(3, Empty, Empty), Empty))

然后我写了这个函数来总结元素......

def sumTree[T <: sum[T]](t: Tree[T]): T = t match {
    case Empty => 0
    case Node(e, left, right) => e + sumTree(left) + sumTree(right)
}

问题

1)为什么我必须定义一个特征(假设我已正确定义)使用“+”?没有它,编译器会将其视为尝试连接字符串。

2)我不知道如何处理“空”。我希望在模式匹配时将“Empty”视为0,但由于通用处理,我不能。是不是我不能用泛型做这个工作?我必须事先指定它接受/返回的类型吗?

1 个答案:

答案 0 :(得分:2)

因为元素类型removeAnnotation()是通用的,所以需要提供一个辅助结构来处理添加。 Scala不知道如何在没有进一步信息的情况下添加T类型的两个元素。这就是您定义特征T的原因。正如您所怀疑的那样,您还需要一种方法来了解零元素是什么。所以你宁愿使用这样的东西:

sum

请注意,我们使用两个操作数定义trait Sum[T] { def zero: T def plus(x: T, y: T): T } 。然后,不要求plusT <: Sum[T]的子类型,而是提供隐式值(称为类型类)。这使得使用任意类型的树更容易,即使元素没有实现Sum

现有的类型类可以实现这个功能,基本上是一个&#34; monoid&#34;。 Scala标准类库具有您可以使用的Numeric。它不仅仅是一个幺半群,定义了几个算术运算,包括Sumzero

然后你的聚合函数变为:

plus

您还可以导入一些语法助手:

def sumTree[T](t: Tree[T])(implicit num: Numeric[T]): T = t match {
  case Empty => num.zero
  case Node(e, left, right) => 
    num.plus(e, num.plus(sumTree(left), sumTree(right)))
}

另见: