2010-06-16 43 views
3

我想添加到所有的集合,它是有道理的,argMax方法。 如何做到這一點?使用implicits?如何用argmax方法擴展Scala集合?

+0

集合不是表達式。它怎麼能有一個argmax方法?你是否想要找到收藏中最大的元素? – 2010-06-16 05:05:33

回答

4

在斯卡拉2.8,這個工程:

val list = List(1, 2, 3) 
def f(x: Int) = -x 
val argMax = list max (Ordering by f) 

正如指出的mkneissl,這不返回最大分數的集合。這是另一個實現,它會嘗試將呼叫數減少到f。如果撥打f並不重要,請參閱mkneissl's answer。另外,請注意他的答案是curried,它提供了優越的類型推斷。

def argMax[A, B: Ordering](input: Iterable[A], f: A => B) = { 
    val fList = input map f 
    val maxFList = fList.max 
    input.view zip fList filter (_._2 == maxFList) map (_._1) toSet 
} 

scala> argMax(-2 to 2, (x: Int) => x * x) 
res15: scala.collection.immutable.Set[Int] = Set(-2, 2) 
+0

令人驚歎!十分感謝。 – 2010-06-16 13:26:21

+0

如果f達到其域的多個元素的最大值,則失敗。考慮x => x * x和x從-2到2 – mkneissl 2010-06-16 14:43:09

+0

@mkneissl啊,我明白了。確實。 – 2010-06-16 16:39:39

1

您可以使用Pimp my Library模式將函數添加到Scala中的現有API。你通過定義一個隱式轉換函數來做到這一點。例如,我有一個類Vector3表示3D矢量:

class Vector3 (val x: Float, val y: Float, val z: Float) 

假設我希望能夠通過編寫像縮放矢量:2.5f * v。我無法在*方法直接添加到Float類ofcourse,但我可以提供這樣的隱式轉換函數:

implicit def scaleVector3WithFloat(f: Float) = new { 
    def *(v: Vector3) = new Vector3(f * v.x, f * v.y, f * v.z) 
} 

注意,此返回包含的結構類型(new { ... }構建體)的目的*方法。

我沒有測試過,但我想你可以做這樣的事情:

implicit def argMaxImplicit[A](t: Traversable[A]) = new { 
    def argMax() = ... 
} 
2

是,通常的方法是使用「皮條客我的圖書館」圖案來裝飾您的收藏。例如(NB就像插圖,並不意味着是一個正確或工作示例):


trait PimpedList[A] { 
    val l: List[A] 

    //example argMax, not meant to be correct 
    def argMax[T <% Ordered[T]](f:T => T) = {error("your definition here")} 
} 

implicit def toPimpedList[A](xs: List[A]) = new PimpedList[A] { 
    val l = xs 
} 

scala> def f(i:Int):Int = 10 
f: (i: Int) Int 

scala> val l = List(1,2,3) 
l: List[Int] = List(1, 2, 3) 

scala> l.argMax(f) 
java.lang.RuntimeException: your definition here 
    at scala.Predef$.error(Predef.scala:60) 
    at PimpedList$class.argMax(:12) 
     //etc etc... 
4

的argmax功能(按照我的理解,從Wikipedia

def argMax[A,B](c: Traversable[A])(f: A=>B)(implicit o: Ordering[B]): Traversable[A] = { 
    val max = (c map f).max(o) 
    c filter { f(_) == max } 
} 

如果你真的想,你可以皮條客它到收藏

implicit def enhanceWithArgMax[A](c: Traversable[A]) = new { 
    def argMax[B](f: A=>B)(implicit o: Ordering[B]): Traversable[A] = ArgMax.argMax(c)(f)(o) 
} 

,並使用它像這樣

val l = -2 to 2 
assert (argMax(l)(x => x*x) == List(-2,2)) 
assert (l.argMax(x => x*x) == List(-2,2)) 

(Scala 2.8)

1

這是隱式生成器模式的一種方法。與以前的解決方案相比,它具有與任何Traversable一起使用的優點,並返回類似的Traversable。可悲的是,這非常重要。如果有人想要,它可能會變成一個相當醜陋的摺疊。

object RichTraversable { 
    implicit def traversable2RichTraversable[A](t: Traversable[A]) = new RichTraversable[A](t) 
} 

class RichTraversable[A](t: Traversable[A]) { 
    def argMax[That, C](g: A => C)(implicit bf : scala.collection.generic.CanBuildFrom[Traversable[A], A, That], ord:Ordering[C]): That = { 
    var minimum:C = null.asInstanceOf[C] 
    val repr = t.repr 
    val builder = bf(repr) 
    for(a<-t){ 
     val test: C = g(a) 
     if(test == minimum || minimum == null){ 
     builder += a 
     minimum = test 
     }else if (ord.gt(test, minimum)){ 
     builder.clear 
     builder += a 
     minimum = test 
     } 
    } 
    builder.result 
    } 
} 

Set(-2, -1, 0, 1, 2).argmax(x=>x*x) == Set(-2, 2) 
List(-2, -1, 0, 1, 2).argmax(x=>x*x) == List(-2, 2) 
0

這是一個基於@ Daniel的接受答案的變體,它也適用於Sets。

def argMax[A, B: Ordering](input: GenIterable[A], f: A => B) : GenSet[A] = argMaxZip(input, f) map (_._1) toSet 

def argMaxZip[A, B: Ordering](input: GenIterable[A], f: A => B): GenIterable[(A, B)] = { 
    if (input.isEmpty) Nil 
    else { 
    val fPairs = input map (x => (x, f(x))) 
    val maxF = fPairs.map(_._2).max 
    fPairs filter (_._2 == maxF) 
    } 
} 

當然,也可以做一個變種,產生(B,Iterable [A])。

0

基於其他答案,你可以很容易地結合每個的優勢(最小的電話f()等)。在這裏,我們對所有Iterables都有一個隱式轉換(因此它們可以透明地調用.argmax()),並且如果由於某種原因,這是首選的獨立方法。 ScalaTest測試引導。

class Argmax[A](col: Iterable[A]) { 
    def argmax[B](f: A => B)(implicit ord: Ordering[B]): Iterable[A] = { 
    val mapped = col map f 
    val max = mapped max ord 
    (mapped zip col) filter (_._1 == max) map (_._2) 
    } 
} 

object MathOps { 
    implicit def addArgmax[A](col: Iterable[A]) = new Argmax(col) 

    def argmax[A, B](col: Iterable[A])(f: A => B)(implicit ord: Ordering[B]) = { 
    new Argmax(col) argmax f 
    } 
} 

class MathUtilsTests extends FunSuite { 
    import MathOps._ 

    test("Can argmax with unique") { 
    assert((-10 to 0).argmax(_ * -1).toSet === Set(-10)) 
    // or alternate calling syntax 
    assert(argmax(-10 to 0)(_ * -1).toSet === Set(-10)) 
    } 

    test("Can argmax with multiple") { 
    assert((-10 to 10).argmax(math.pow(_, 2)).toSet === Set(-10, 10)) 
    } 
}