2011-11-22 138 views
16

说,我们要做出这样的函数minBy返回等于极简的所有元素集合在返回原来的集合类型而不是我们最初的List在泛型方法

所以我试图改变签名

def multiMinBy[A, B: Ordering, C <: Traversable[A]](xs: C)(f: A => B) 

,希望我可能会得到一个C回来,而不是一个Traversable[A]。但是,我没有得到任何回报:

scala> multiMinBy(List("zza","zzza","zzb","zzzb"))(_.last) 

<console>:9: error: inferred type arguments [Nothing,Nothing,List[java.lang.String]] 
do not conform to method multiMinBy's type parameter bounds [A,B,C <: Traversable[A]] 

我想这是因为我们有C出现在参数之前A已经推断?所以,我翻的参数的顺序,并且加入了铸:

def multiMinBy[A, B: Ordering, C <: Traversable[A]](f: A => B)(xs: C) = { 
    val minVal = f(xs minBy f) 
    (xs filter (f(_) == minVal)).asInstanceOf[C] 
} 

其作品,除了我们要这样称呼它:

multiMinBy((x: String) => x.last)(List("zza","zzza","zzb","zzzb")) 

有没有保留原来的语法的方式,同时获得正确的收集类型?

回答

20

我认为迈尔斯萨宾解决方案太复杂。 Scala的收藏已经有必要的机制,使其工作,具有非常小的变化:

import scala.collection.TraversableLike 
def multiMinBy[A, B: Ordering, C <: Traversable[A]] 
       (xs: C with TraversableLike[A, C]) 
       (f: A => B): C = { 
    val minVal = f(xs minBy f) 
    xs filter (f(_) == minVal) 
} 
+3

是的,我同意,这是比我更好的解决方案。 –

10

你的问题是,作为GenTraversable[A]的方法(我将在这个答案,而不是使用Traversable[A])观看时filter方法的结果类型为不超过GenTraversable[A]更精确。不幸的是,在multiMinBy方法的正文中,写出了关于xs的所有信息。

为了得到结果,您需要更精确地签署multiMinBy。这样做同时还留下容器类型相对开放的一个方法是如下使用结构类型,

type HomFilter[CC[X] <: GenTraversable[X], A] = 
    CC[A] { def filter(p : A => Boolean) : CC[A] } 

def multiMinBy[CC[X] <: GenTraversable[X], A, B: Ordering] 
    (xs: HomFilter[CC, A])(f: A => B) : CC[A] = { 
    val minVal = f(xs minBy f) 
    xs filter (f(_) == minVal) 
    } 

结构类型HomFilter使我们能够断言参数multiMinBy必须有一个filter方法与所需结果类型。

样品REPL会话,

scala> val mmb = multiMinBy(List("zza","zzza","zzb","zzzb"))(_.last) 
mmb: List[String] = List(zza, zzza) 

请记住,这是比容器只是Traversable更严格的要求:它允许为GenTraversable亚型定义filter方法,这都是不正规的以这种方式。上面的签名会静态地阻止这些类型的值被传递给multiMinBy ......大概这就是你之后的行为。

+0

人能避免整个结构打字啄只需使用'TraversableLike'。 –

13

如何使用CanBuildFrom

import scala.collection.immutable._ 
import scala.collection.generic._ 

def multiMinBy[A, B, From[X] <: Traversable[X], To](xs: From[A])(f: A => B) 
    (implicit ord: Ordering[B], bf: CanBuildFrom[From[_], A, To]) = { 
    val minVal = f(xs minBy f) 
    val b = bf() 
    b ++= (xs filter (f(_) == minVal)) 
    b.result 
} 



scala> multiMinBy(List("zza","zzza","zzb","zzzb"))(_.last) 
res1: List[java.lang.String] = List(zza, zzza)