如何在scala中对数字数据类型的序列进行排序

时间:2019-09-01 15:39:27

标签: scala

我是scala的新手,为了练习,我尝试编写一个通用函数来计算整数或双精度数的中位数。 下面是代码片段:

implicit class trial[T](seq: Seq[T]) {
    def median[T](implicit num: Fractional[T]):Double = {
      import num._

      seq.sorted match {

        case x if x.length % 2 != 0 => x(x.length / 2).toDouble()
        case x => (x(x.length / 2).toDouble() + x(x.length / 2 - 1).toDouble()) / 2

      }

    }
  }

在上面的代码中,排序功能不起作用,表示找不到隐式排序。有人可以指导我如何在scala中对数字列表进行排序吗?

编辑==>

查看完这篇文章的答案后,我已经更改了代码(更改后的代码粘贴在下面),现在它适用于Ints和Doubles,但是现在如果将来如果我必须支持BigDecimals,那么我将必须进行哪些更改做吗?

implicit class GenericMedian[T](seq: Seq[T]) {
    def median(implicit num: Numeric[T]) = {
      import num._
      val medianPosition = seq.length / 2
      seq.sortWith(num.gt) match {
        case x if x.length % 2 != 0 => x(medianPosition)
        case x => (x(medianPosition).toDouble() + x(medianPosition - 1).toDouble()) / 2
      }

    }
  }

2 个答案:

答案 0 :(得分:1)

这对我来说适用于 Scala 2.13

implicit class trial[T](private val seq: Seq[T]) extends AnyVal {
  def median(implicit num: Integral[T]): T = {
    import num._

    val sorted = seq.sorted
    val length = sorted.length
    val medianPosition = length / 2

    if ((length % 2) == 0)
      sorted(medianPosition)
    else
      (sorted(medianPosition) + sorted(medianPosition + 1)) / num.fromInt(2)
  }
}

我忘了提到您的代码(至少在编译方面)的唯一真正问题是,因为您要在代码的定义中定义一个新的T类型变量方法。您不能使用该隐式对序列进行排序,因为对于编译器而言,它们是两种不同的类型。

答案 1 :(得分:1)

以下使用Numeric类型类来处理整数和双精度数

implicit class MedianSeq[T](seq: Seq[T]) {
  def median(implicit num: Numeric[T]): Option[Double] = {
    val sorted = seq.sorted
    val fractionalMidpoint: Double = sorted.size / 2.0

    sorted.size match {
      case x if x < 2 => None
      case x if x == 2 => Some(num.toDouble(num.plus(seq(0), seq(1))) / 2)
      case x =>
        if (fractionalMidpoint % 2 != 0.0) {
          Some(num.toDouble(sorted(fractionalMidpoint.toInt)))
        } else {
          val a = sorted(fractionalMidpoint.toInt - 1)
          val b = sorted(fractionalMidpoint.toInt)
          Some(num.toDouble(num.plus(a, b)) / 2)
        }
    }
  }
}

输出

Seq(3,5,2,34,5,6,7,87,8).median == Some(6.0) // true
Seq(1,2,3,4).median == Some(2.5) // true
Seq(1.0,2.0,3.0,4.0).median == Some(2.5) // true
Seq(1,2).median == Some(1.5) // true
Seq(1,1).median == Some(1.0) // true
Seq(1).median == None // true

请注意,当元素数为偶数时,我们使用this中位数的定义:

  

当有两个中间数字时,我们将它们取平均值。

因此Seq(1,2).median == Some(1.5)