假设我们想要创建一个像minBy
这样的函数来返回集合中所有相同极简主义的元素:
def multiMinBy[A, B: Ordering](xs: Traversable[A])(f: A => B) = {
val minVal = f(xs minBy f)
xs filter (f(_) == minVal)
}
scala> multiMinBy(List("zza","zzza","zzb","zzzb"))(_.last)
res33: Traversable[java.lang.String] = List(zza, zzza)
到目前为止,非常好,除了我们有一个Traversable
而不是我们的初始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"))
有没有办法保留原始语法,同时获得正确的集合类型?
答案 0 :(得分:22)
我认为Miles Sabin解决方案过于复杂。 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)
}
答案 1 :(得分: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)
答案 2 :(得分:10)
您的问题是,当GenTraversable[A]
上的方法被查看时(我将在此回答中使用而不是Traversable[A]
)filter
方法的结果类型为no比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
方法,而不是{39}}经常这样。上面的签名将静态地阻止此类型的值传递给multiMinBy
...可能是您之后的行为。