Написание общей функции в Scala
Я пытаюсь написать обобщенную среднюю функцию, которая работает с Iterable, которая содержит числовые типы. Он будет работать, скажем, на массивах, так:
val rand = new scala.util.Random()
val a = Array.fill(1000) { rand.nextInt(101) }
val b = Array.fill(1000) { rand.nextDouble }
println(mean(a))
println(mean(b))
и т.д., надеемся, что он сможет работать с другими итерами, такими как списки.
Я пробовал различные заклинания для среднего метода, безрезультатно:
def mean[T <% Numeric[T]](xs: Iterable[T]) = xs.sum.toDouble / xs.size
def mean[A](xs: Iterable[Numeric[A]]):Double = xs.sum.toDouble / xs.size
def mean[T](xs: Iterable[T])(implicit num: Numeric[T]):Double = xs.sum / xs.size
def mean(xs: Iterable[Double]) = xs.sum / xs.size
Каков правильный способ сделать это в Scala?
Ответы
Ответ 1
Это работает:
def mean[T : Numeric](xs: Iterable[T]): T = implicitly[Numeric[T]] match {
case num: Fractional[_] => import num._; xs.sum / fromInt(xs.size)
case num: Integral[_] => import num._; xs.sum / fromInt(xs.size)
case _ => sys.error("Undivisable numeric!")
}
Итак, давайте сделаем некоторые объяснения. Во-первых, Numeric
должен использоваться в шаблоне типа. То есть вы не говорите, что тип T
есть или может быть преобразован в Numeric
. Вместо этого Numeric
предоставляет методы по типу T
. Одним из таких примеров является num.fromInt
.
Далее, Numeric
не предоставляет общий оператор деления. Вместо этого нужно выбирать между Fractional
и Integral
. Здесь я сопоставляю Numeric[T]
, чтобы различать оба.
Обратите внимание, что я не использую T
в матче, потому что Scala не может проверять параметры типа на совпадения, поскольку они стираются. Вместо этого я использую _
, а Scala указывает правильный тип, если это возможно (как здесь).
После этого я импортирую num._
, где num
либо Fractional
, либо Integral
. Это приводит к некоторым неявным преобразованиям в контекст, который позволяет мне делать такие вещи, как вызов метода /
напрямую. Если бы я не делал этого импорта, я был бы вынужден написать это:
num.div(xs.sum, num.fromInt(xs.size))
Обратите внимание, что мне не нужно передавать неявный параметр в xs.sum
, так как он уже неявно доступен в области.
Я предполагаю, что это так. Я что-то пропустил?
Ответ 2
Одна из ваших версий довольно близка:
def mean[T](xs: Iterable[T])(implicit num: Numeric[T]):Double =
num.toDouble(xs.sum) / xs.size
Вот другой синтаксис:
def mean[T: Numeric](xs: Iterable[T]):Double =
implicitly[Numeric[T]].toDouble(xs.sum) / xs.size
Ответ 3
def mean[A](it:Iterable[A])(implicit n:Numeric[A]) = {
it.map(n.toDouble).sum / it.size
}
Ответ 4
Это довольно старый вопрос, но я в основном делаю это
def average[A](list: List[Any])(implicit numerics: Numeric[A]): Double = {
list.map(Option(_)).filter(_.isDefined).flatten match {
case Nil => 0.0
case definedElements => numerics.toDouble(list.map(_.asInstanceOf[A]).sum) / definedElements.length.toDouble
}
}
для списка, который может содержать значения null
(я должен поддерживать совместимость с Java). Элементы null
не учитываются в среднем.