意味のあるすべてのコレクションにargMaxメソッドを追加したいと思います。どうやってするの?暗黙を使用しますか?
8 に答える
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 の回答を参照してください。また、彼の答えはカリー化されていることに注意してください。これにより、優れた型推論が提供されます。
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)
argmax 関数 (ウィキペディアから理解できるように)
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))
(スカラ2.8)
はい、通常は「pimp my library」パターンを使用してコレクションを装飾します。例(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...
Pimp my Libraryパターンを使用して、Scala の既存の API に関数を追加できます。これを行うには、暗黙的な変換関数を定義します。たとえば、Vector3
3D ベクトルを表すクラスがあります。
class Vector3 (val x: Float, val y: Float, val z: Float)
次のように記述してベクトルをスケーリングできるようにしたいとします2.5f * v
。*
もちろん、メソッドをクラスに直接追加することはできませんFloat
が、次のような暗黙の変換関数を提供できます。
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() = ...
}
暗黙的なビルダー パターンを使用してこれを行う方法を次に示します。以前のソリューションよりも、任意の 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の受け入れられた回答に大まかに基づいたバリアントであり、セットでも機能します。
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()
など)。ここでは、すべての Iterable の暗黙的な変換 (.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))
}
}