Написание универсальной средней функции в Scala - PullRequest
11 голосов
/ 31 мая 2011

Я пытаюсь написать универсальную среднюю функцию, которая работает на 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?

Ответы [ 4 ]

17 голосов
/ 31 мая 2011

Это работает:

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, поскольку он уже неявно доступен всфера.

Наверное, все.Я что-то пропустил?

7 голосов
/ 31 мая 2011

Одна из ваших версий довольно близка:

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
4 голосов
/ 31 мая 2011
def mean[A](it:Iterable[A])(implicit n:Numeric[A]) = {
  it.map(n.toDouble).sum / it.size
}
0 голосов
/ 12 июня 2016

Это довольно старый вопрос, но я в основном делаю это

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 не учитываются в среднем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...