Generics 在泛型方法中返回原始集合类型

Generics 在泛型方法中返回原始集合类型,generics,scala,collections,Generics,Scala,Collections,假设我们想创建一个函数,如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[ja

假设我们想创建一个函数,如
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)
到目前为止,一切都很好,只是我们有一个
可遍历的
返回,而不是我们最初的
列表

所以我试着把签名改成

def multiMinBy[A, B: Ordering, C <: Traversable[A]](xs: C)(f: A => B)
我认为这是因为在推断出
A
之前,在参数中出现了
C
?所以我改变了参数的顺序,添加了一个cast:

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]
}

有没有一种方法可以在恢复正确的集合类型的同时保留原始语法?

您的问题是,当在
GenTraversable[a]
上被视为一种方法时(在这个答案中,我将使用它而不是
Traversable[a]
过滤器的结果类型并不比
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)
  }

请记住,这是一个比容器仅可
遍历
更严格的要求:允许
GenTraversable
的子类型以这种方式定义不规则的
筛选
方法。上面的签名将静态阻止此类类型的值传递给
multiMinBy
。。。大概这就是您所追求的行为。

使用
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)
导入scala.collection.immutable_
导入scala.collection.generic_
def multiMinBy[A,B,From[X]B)
(隐式ord:Ordering[B],bf:CanBuildFrom[From[]A,To])={
val minVal=f(xs minBy f)
val b=bf()
b++=(xs过滤器(f()=minVal))
b、 结果
} 
scala>multiMinBy(列表(“zza”、“zzza”、“zzb”、“zzzb”))(最后一个)
res1:List[java.lang.String]=List(zza,zzza)

我认为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)
}
导入scala.collection.TraversableLike
def multiMinBy[A,B:排序,C B):C={
val minVal=f(xs minBy f)
xs过滤器(f()=最小值)
}

只要使用
TraversableLike
就可以避免整个结构类型。是的,我同意,这是一个比我更好的解决方案。你能解释一下为什么必须使用GreaterLowerBound:
C和TraversableLike[a,C]
。你怎么知道要让方法返回C需要这个?我想我不知道输入的具体类型在哪里saved@Adrian对于Scala 2.13,这可能不再是真的了。事实上,我对此表示怀疑。
Traversable[A]
A
A
TraversableLike[A,C]授予仅依赖于项类型的绑定方法,授予的方法不仅处理类型项类型
A
,而且返回类型
C
。因为它是
C,具有TraversableLike[A,C]
,这意味着我可以对它调用一个方法,它将返回相同的集合
C
,而不是,比如说,返回
Traversable[a]
。这就是
filter
方法的情况,如果没有这个绑定,它将不会返回
C
。啊,这与union-oh方法(来自Traversable[a]的方法)有关从TraversableLike[A,C])@Adrian不是方法的联合,而是类型的统一两者都存在,但
TraversableLike
上的返回类型更为具体。我可能只使用了
TraversableLike
,但我猜——我真的不记得了——如果我这样做,类型推断就不起作用了。
scala> val mmb = multiMinBy(List("zza","zzza","zzb","zzzb"))(_.last)
mmb: List[String] = List(zza, zzza)
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)
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)
}