如何找到最接近值列表的值?

时间:2014-01-08 11:49:23

标签: scala

我试图编写一个返回值列表中最接近的值的算法。所以在List(4.0,6.0,2.0)中,最接近7.0的值是6.0

以下是我使用的代码,但由于返回的最接近的值为4,因此不正确:

如何解决以下代码或是否有可用于解决此问题的Scala实用程序方法?

    val num = 7.0                             //> num  : Double = 7.0
    val listNums = List[Double](4,6,2)        //> listNums  : List[Double] = List(4.0, 6.0, 2.0)


    def getClosest(num : Double , listNums : List[Double]) = {

      var min = java.lang.Double.MAX_VALUE
    var closest = num

    for(x <- listNums ){

    val diff = x - num
   if(num < min){
    min = diff
    closest = x
    }
   }

   closest

    }                                         //> getClosest: (num: Double, listNums: List[Double])Double

val closest = getClosest(num , listNums)  //> closest  : Double = 4.0

4 个答案:

答案 0 :(得分:16)

这几乎是一行minBy

def getClosest(num: Double, listNums: List[Double]) =
  listNums.minBy(v => math.abs(v - num))
不幸的是,

minBy部分函数 - 当在空列表中调用时,它会因异常而崩溃。要匹配实现的行为,可以编写以下内容:

def getClosest(num: Double, listNums: List[Double]) = listNums match {
  case Nil => Double.MaxValue
  case list => list.minBy(v => math.abs(v - num))
}

您的代码存在的问题是您没有采用绝对值,因为另一个答案暗示指出。不要使用Math.abs,这是java.lang.Math.abs的简写。 math.abs更具惯用性。

答案 1 :(得分:3)

一个简单的实现是:

def getClosest(num : Double , list : List[Double]) :Double = list match {
    case x :: xs => list.foldLeft(x){(ans,next) => 
                     if(math.abs(next - num) < math.abs(ans - num)) next else ans }
    case Nil => throw new RuntimeException("Empty list")
  }

scala> getClosest(20, List(1,19,22,24))
res0: Double = 19.0

更通用的实施方式是:

  def getClosest[A: Numeric](num: A, list: List[A]): A = {
    val im = implicitly[Numeric[A]]
    list match {
      case x :: xs => list.minBy (y => im.abs(im.minus(y, num)))
      case Nil => throw new RuntimeException("Empty list")
    }
  }  

感谢@Travis建议minBy。它比foldLeft

更漂亮

答案 2 :(得分:0)

根据与所选号码的距离定义Ordering,并使用List中的min算法来应用此排序。

我现在没有scala安装方便提供经过编码测试的解决方案,但这两个方面相对容易。

答案 3 :(得分:0)

如果您想要更通用,可以试试这个:

def getClosest[A: Numeric](value: A, elems: Traversable[A]): A = {
  val ops = implicitly[Numeric[A]]
  elems.minBy(e => ops.abs(ops.minus(e, value)))
}

然后:

getClosest(20, List(1, 19, 22, 24))         
    //> res1: Int = 19
getClosest(BigDecimal("5000000000000000"), List(BigDecimal(1), BigDecimal(19)))
    //> res2: scala.math.BigDecimal = 19