8

私は一般的な数学演算にAzaveaNumericScalaライブラリを使用しています。ただし、これらはScalaコレクションAPIで使用できません。これは、Scala Numericが必要であり、2つのNumericが相互に排他的であるように見えるためです。すべてのタイプに両方の数値のコンテキスト境界があることを要求する以外に、Azavea数値のScalaコレクションですべての数学演算を再実装することを回避できる方法はありますか?

import Predef.{any2stringadd => _, _}

class Numeric {
  def addOne[T: com.azavea.math.Numeric](x: T) {
    import com.azavea.math.EasyImplicits._
    val y = x + 1 // Compiles

    val seq = Seq(x)
    val z = seq.sum // Could not find implicit value for parameter num: Numeric[T]
  }
}

AzaveaNumericが次のように定義されている場合

trait Numeric[@scala.specialized A] extends java.lang.Object with 

com.azavea.math.ConvertableFrom[A] with com.azavea.math.ConvertableTo[A] with scala.ScalaObject {
   def abs(a:A):A
   ...remaining methods redacted...
}

object Numeric {
  implicit object IntIsNumeric extends IntIsNumeric
  implicit object LongIsNumeric extends LongIsNumeric
  implicit object FloatIsNumeric extends FloatIsNumeric
  implicit object DoubleIsNumeric extends DoubleIsNumeric
  implicit object BigIntIsNumeric extends BigIntIsNumeric
  implicit object BigDecimalIsNumeric extends BigDecimalIsNumeric

  def numeric[@specialized(Int, Long, Float, Double) A:Numeric]:Numeric[A] = implicitly[Numeric[A]]
}
4

2 に答える 2

4

優れたRégisJean-Gillesソリューションを使用して、AzaveaのNumericをラップすることができます。自分でメソッドを再作成することもできますが、Azaveaの数値を使用します。NumericRangeを除けば、ほとんどの場合、実装は非常に簡単です。

ただし、Azaveaの数値ライブラリを継承するSpireに興味があるかもしれません。すべて同じ機能がありますが、いくつかの新しい機能もあります(より多くの操作、新しい数値タイプ、並べ替えと選択など)。2.10を使用している場合(ほとんどの作業は2.10に向けられています)、Spire's Numericを使用すると、一般的なアプローチのオーバーヘッドが実質的にすべて排除され、多くの場合、直接(非一般)実装と同じくらい高速に実行されます。

そうは言っても、あなたの質問は良い提案だと思います。toScalaNumericNumericにメソッドを追加する必要があります。どのScala収集方法を使用する予定でしたか?Spireは、qsum、qproduct、qnorm(p)、qsort、qselect(k)などのいくつかの新しいメソッドを配列に追加します。

于 2012-10-29T14:25:08.977 に答える
2

最も一般的な解決策は、それに関してラップcom.azavea.math.Numericして実装するクラスを作成することです。scala.math.Numeric

class AzaveaNumericWrapper[T]( implicit val n: com.azavea.math.Numeric[T] ) extends scala.math.Numeric {
  def compare (x: T, y: T): Int = n.compare(x, y)
  def minus (x: T, y: T): T = n.minus(x, y)
  // and so on
}

次に、暗黙的な変換を実装します。

// NOTE: in scala 2.10, we could directly declare AzaveaNumericWrapper as an implicit class
implicit def toAzaveaNumericWrapper[T]( implicit n: com.azavea.math.Numeric[T] ) = new AzaveaNumericWrapper( n )

ここでは、それ自体が暗黙的であるという事実nが重要です。これにより、型の暗黙的値が予期されるcom.azavea.math.Numeric場合に、型の暗黙的値を自動的に使用できますscala.math.Numeric。完全にするために、おそらく逆も行う必要があることに注意してください(scala.math.Numericの観点からcom.azavea.math.Numericを実装するクラスScalaNumericWrapperを記述します)。

ここで、上記のソリューションには欠点があります。呼び出しごとに変換(したがってインスタンス化)が得られます(コンテキストが型scala.math.Numericにバインドされ、のインスタンスのみcom.azavea.math.Numericがスコープ内にあるメソッドへの変換)。したがって、実際には、数値タイプごとにAzaveaNumericWrapperの暗黙的なシングルトンインスタンスを定義する必要があります。MyTypeタイプがありMyOtherType、次のインスタンスを定義したと仮定しますcom.azavea.math.Numeric

implicit object MyTypeIsNumeric extends AzaveaNumericWrapper[MyType]
implicit object MyOtherTypeIsNumeric extends AzaveaNumericWrapper[MyOtherType]
//...

また、azaveaのNumericクラスの明らかな主な目的は、実行速度を大幅に向上させることであることに注意してください(主に型パラメーターの特殊化による)。上記のようにラッパーを使用すると、スペシャライゼーションが失われるため、スペシャライゼーションから得られる速度が失われます。スペシャライゼーションはずっと使用する必要があり、スペシャライズされていないジェネリックメソッドを呼び出すとすぐに、スペシャライズされていないジェネリックの世界に入ります(そのメソッドがスペシャライズドメソッドを呼び出したとしても)。したがって、速度が重要な場合はNumeric、scalaの代わりにazaveaを直接使用してみてくださいNumeric(AzaveaNumericWrapperが内部で使用しているからといって、ここでは特殊化が行われないため、速度が上がるわけではありません)。

私の例では、AzaveaNumericWrapperfor型Intなどのインスタンスを定義することを避けたことにお気づきかもしれませんLong。これは、これらのタイプのscala.math.Numericの暗黙の値が(標準ライブラリに)すでに存在するためです。import scala.math.Numeric.{ShortIsIntegral => _}独自の(azaveaでバックアップされた)バージョンが使用されていることを確認するために、(のようなものを介して)それらを非表示にしたくなるかもしれませんが、意味がありません。私が考えることができる唯一の理由は、それをより速く実行することですが、上で説明したように、それはしません。

于 2012-10-29T14:01:04.510 に答える