7

在 Scala 中,我希望能够编写使用 >、/、* 等运算符的通用类,但我不知道如何约束 T 以使其起作用。

我研究了用 Ordered[T] 约束 T,但这似乎不起作用,因为只有 RichXXX(例如 RichInt)扩展它,而不是 Int 等。我还看到了 Numeric[T],这是否仅在 Scala 2.8 中可用?

这是一个具体的例子:

class MaxOfList[T](list: List[T] ) {
  def max = {
    val seed: Option[T] = None

    list
      .map( t => Some(t))
      // Get the max      
      .foldLeft(seed)((i,m) => getMax(i,m) )
  }

  private def getMax(x: Option[T], y: Option[T]) = {
    if ( x.isDefined && y.isDefined )
      if ( x > y ) x else y
    else if ( x.isDefined )
      x
    else
      y
  }
}

这个类不会编译,因为有很多不支持 > 等的 T。

想法?

现在我已经使用了 MixIn 特征来解决这个问题:

/** Defines a trait that can get the max of two generic values
 */
trait MaxFunction[T] {
  def getMax(x:T, y:T): T
}

/** An implementation of MaxFunction for Int
 */
trait IntMaxFunction extends MaxFunction[Int] {
  def getMax(x: Int, y: Int) = x.max(y)
} 

/** An implementation of MaxFunction for Double
 */
trait DoubleMaxFunction extends MaxFunction[Double] {
  def getMax(x: Double, y: Double) = x.max(y)
} 

如果我们改变原始类,可以在实例化时混合。

PS Mitch,受您重写 getMax 的启发,这里是另一个:

  private def getMax(xOption: Option[T], yOption: Option[T]): Option[T] = (xOption,yOption) match {
    case (Some(x),Some(y)) => if ( x > y ) xOption else yOption
    case (Some(x), _) => xOption
    case _ => yOption
  }
4

3 回答 3

9

您可以使用View Bounds

简而言之,def foo[T <% U](t: T)它是一个函数,它可以将任何 T 隐式转换为或可以隐式转换为 U。由于 Int 可以转换为 RichInt(其中包含您所需的方法),因此这是一个很好的用法示例。

class MaxOfList[T <% Ordered[T]](list: List[T] ) {
  def max = {
    val seed: Option[T] = None
    list.foldLeft(seed)(getMax(_,_))
  }

  private def getMax(xOption: Option[T], y: T) = (xOption, y) match {
    case (Some(x), y) if ( x > y ) => xOption
    case (_, y) => Some(y)
  }
}

PS - 我重写了你的 getMax(...) 方法来比较值而不是选项本身,并使用模式匹配而不是 isDefined(...)

PPS - Scala 2.8 将具有可能有用的数字特征。http://article.gmane.org/gmane.comp.lang.scala/16608


附录

只是为了咯咯笑,这是完全消除 getMax 方法的超紧凑版本:

class MaxOfList[T <% Ordered[T]](list: List[T] ) {
  def max = list.foldLeft(None: Option[T]) {
      case (Some(x), y) if ( x > y ) => Some(x)
      case (_, y) => Some(y)
  }
}

又一个附录

这个版本对于大型列表会更有效......避免为每个元素创建 Some(x) :

class MaxOfList[T <% Ordered[T]](list: List[T] ) {
  def max = {
    if (list.isEmpty) None
    else Some(list.reduceLeft((a,b) => if (a > b) a else b))
  }
}

最后一个,我保证!

此时,您可以放弃该类并使用一个函数:

  def max[T <% Ordered[T]](i: Iterable[T]) = {
    if (i.isEmpty) None
    else Some(i.reduceLeft((a,b) => if (a > b) a else b))
  }
于 2009-12-09T01:05:02.283 回答
2

Numeric[T]Scala 2.8 中,

scala> case class MaxOfList[T : Numeric](list: List[T]) {
     |     def max = if(list.isEmpty) None else Some(list.max)
     | }
defined class MaxOfList

scala> MaxOfList(1::2::3::9::7::Nil).max

res1: Option[Int] = Some(9)

scala> MaxOfList(1.5::3.9::9.2::4.5::Nil).max

res2: Option[Double] = Some(9.2)

scala> MaxOfList(Nil: List[Byte]).max

res3: Option[Byte] = None

scala>
于 2009-12-09T05:04:20.693 回答
1

正如Mitch Blevins所回答的,这就是视图边界的用途。现在,虽然Numeric只是在 Scala 2.8 上添加了一些东西,但并不是说它不依赖于任何 Scala 2.8 功能。人们可以在 Scala 2.7 上做同样的事情。

您可能对我为Rosetta Code编写的一些通用代码片段感兴趣:

于 2009-12-09T14:19:50.663 回答