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)
} 

元のクラスを変更すると、インスタンス化時にミックスインできます。

getMaxの書き直しに触発されたPSMitchは、次のとおりです。

  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

ビュー境界を使用できます。

要するに、def foo[T <% U](t: T)はUに暗黙的に変換される、または変換できる任意のTを受け取る関数です。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

Scala Numeric[T]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にのみ追加されたものですが、Scala2.8の機能に依存していないわけではありません。Scala2.7でも同じことができます。

私がRosettaCode用に書いたいくつかの一般的なコードスニペットに興味があるかもしれません:

于 2009-12-09T14:19:50.663 に答える