我想添加到所有的集合,它是有道理的,argMax方法。 如何做到這一點?使用implicits?如何用argmax方法擴展Scala集合?
回答
在斯卡拉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)
令人驚歎!十分感謝。 – 2010-06-16 13:26:21
如果f達到其域的多個元素的最大值,則失敗。考慮x => x * x和x從-2到2 – mkneissl 2010-06-16 14:43:09
@mkneissl啊,我明白了。確實。 – 2010-06-16 16:39:39
您可以使用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() = ...
}
是,通常的方法是使用「皮條客我的圖書館」圖案來裝飾您的收藏。例如(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...
的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)
這是隱式生成器模式的一種方法。與以前的解決方案相比,它具有與任何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)
這是一個基於@ 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])。
基於其他答案,你可以很容易地結合每個的優勢(最小的電話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))
}
}
- 1. 擴展Scala集合
- 2. 擴展一個Scala集合
- 3. 使用新屬性擴展scala集合
- 4. 如何用成員值擴展Scala集合?
- 5. 如何在泛型集合上創建擴展方法
- 6. 結合擴展方法
- 7. 關於擴展Scala方法簽名
- 8. Scala添加方法擴展東西
- 9. 用方法豐富Scala集合
- 10. 收集隨機使用擴展方法
- 11. 將一個集合轉換爲另一個集合的通用擴展方法
- 12. 如何使用擴展方法使集合成爲「n」個元素?
- 13. 使用擴展方法的擴展類
- 14. 集成的Java使用Scala:從Scala類擴展
- 15. 在Scala中擴展帶額外字段的集合類
- 16. Scala中可擴展的自定義集合類型
- 17. 特定成員類型的擴展Scala集合
- 18. 通過scala反射的應用方法實例化scala集合
- 19. 如何編寫使用擴展方法
- 20. 如何定義通用擴展方法
- 21. Safari擴展 - 如何使用setContextEventUserInfo方法
- 22. 如何從另一個php擴展方法調用php擴展方法
- 23. 擴展方法和新發布的擴展程序集
- 24. 擴展方法
- 25. 擴展方法
- 26. 在迭代基類集合時解決子類擴展方法
- 27. 擴展內置集合,問題與內置的方法
- 28. Rails的協會擴展:重寫HABTM分配(集合=)方法
- 29. 自定義集合擴展列表<T>添加方法
- 30. System.Collections.Concurrent集合上的擴展方法是線程安全的?
集合不是表達式。它怎麼能有一個argmax方法?你是否想要找到收藏中最大的元素? – 2010-06-16 05:05:33