2017-05-23 19 views
0

こんにちは、次のようにしてください。 シーケンスの平均を計算する関数であると考えられます。 Scalaの汎用型 `toDouble`

def fun1[T <: { def toDouble:Double }] (seq:Seq[T]) = { 
    val q2 = seq map { _.toDouble } 
    val n = q2.size.toDouble 
    q2.sum/n 
    } 

は、しかし、私は fun1Seq(1,2,3)を渡すことはできません。 なぜですか?

答えて

0
def fun1[T <: AnyVal{ def toDouble:Double }] (seq:Seq[T]) = { 
    val q2 = seq map { _.toDouble } 
    val n = q2.size.toDouble 
    q2.sum/n 
} 

fun1(Seq(1, 2, 3)) 

コンパイラがAnyRefとして型を推測するようです。 AnyValとして定義する。 AnyRefはすべてのオブジェクトのスーパータイプですが、AnyValはすべてのプリミティブのスーパータイプです。

+2

'{def foo:Foo}'は 'AnyRef {def foo:Foo}'の略です。 'AnyRef'以外のものが必要な場合は、明示的に名前を付ける必要があります。 –

+0

@sheunis「動作しているように見える」というのは、基本的に 'AnyRef'にはプリミティブ型が含まれず、' AnyVal'には基本的な型が含まれていないということです。 – flavian

+0

@flavianありがとう、私は同意します。私は編集をしました。 – sheunis

1

構造型はJVM上で非常に高価なので、避けてください。これは、NumericのみがDoubleとしてエンコードされる可能性があることを意味します。 ScalaはNumeric typeclassを、いわゆる "数値型"で一般的な操作を定義する方法として提供しています。したがって、あなたが提案しているものを実際に使用する必要はありません。

def fun1[T](seq: Seq[T])(ev: Numeric[T]): Double = { 
    val q2 = seq map ev.toDouble 
    val n = q2.size.toDouble 
    q2.sum/n 
} 

あなたが望むすべてが数字上の汎用であるためにあなたの操作であればあなたは本当にそれより創造的な取得する必要はありません。それ以外の場合は、独自の型クラスを実装する必要があります。今

trait Converter[T] { 
    def toDouble(source: T): Double 
} 
object Converter { 
    implicit def numericConvert[T : Numeric]: Converter[T] = new Converter[T] { def toDouble(source: T): Double = implicitly[Numeric[T]].toDouble(source)} 
    implicit object StringConverter extends Converter[String] = ... 
} 

Numericに基づく単純なアプローチを使用するには、簡単なヘルパーを定義することができます。

object Helper { 
    implicit class ColOps[ 
    M[X] <: TraversableOnce[X], 
    T 
    ](val col: M[T])(implicit ev: Numeric[T]) { 
    def average: Double = { 
     val q2 = col map ev.toDouble 
     val n = q2.size.toDouble 
     q2.sum/n 
    } 
    } 
} 

今、すべてを行う必要がある。我々はあまりにも一般的なコレクション型の上に抽象できるので

import Helper._ 

    List(1, 2, 3).average 
    Seq(1, 2, 3).average 
    Set(5F, 2F, 3F).average 

これは、もう少しクールです。