2014-01-08 57 views
2

我想寫值的列表中返回最接近值的算法。因此,在一個列表(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 
+0

只是一個查詢對此。如果列表像這樣listNums = List [Double](4,6,2,8)會怎麼樣?現在6和8都是最接近的! –

+0

@Anuj Mehta我只希望第一個匹配的被退回 –

回答

11

這幾乎是一個班輪與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.absmath.abs比較習慣。

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

+0

鑑於存在'minBy','math.abs'應該比'Math.abs'更受歡迎,所以真的不需要寫一個手動摺疊。 –

+0

@TravisBrown是同意:)。因此,我高舉你的答案! – Jatin

+1

另一個注意事項 - 如果你真的想要,你可以通過導入'Numeric.Implicits._'和'Ordering.Implicits._'來在你的通用版本中使用標準的操作符。 –

0

更漂亮,從你選擇的數量來定義Ordering的距離,並使用min馬託從List應用此排序。

我沒有一階安裝方便,現在提供一個編碼的測試解決方案,但這樣做的兩件相對容易。

+1

定義這種Ordering'實例的''爲Double'是不是真的乾淨或慣用的使用'minBy',其中完成同樣的事情。 –

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