考虑一个对集合distinctBy
进行操作的简单函数,就像distinct
删除“重复”一样(这不是必需的实际副本):
import scala.collection.TraversableLike
import scala.collection.generic.CanBuildFrom
import scala.collection.mutable.{Set=>MSet}
def distinctBy[T,R,Coll]
(xs: Coll)
(f: T => R)
(implicit ev: Coll <:< TraversableLike[T,Coll],
cbf: CanBuildFrom[Coll,T,Coll]): Coll = {
val builder = cbf(xs)
builder.sizeHint(xs.size)
val seen = MSet.empty[R]
xs.foreach { elem =>
if(!seen(f(elem))){
builder += elem
}
}
builder.result()
}
现在考虑使用它的类:
case class X(i: Int, j: Int)
天真地使用此功能失败:
scala> distinctBy(Vector(X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
<console>:14: error: missing parameter type for expanded function ((x$1) => x$1.i)
distinctBy(Vector(X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
^
<console>:14: error: Cannot construct a collection of type scala.collection.immutable.Vector[X] with elements of type Any based on a collection of type scala.collection.immutable.Vector[X].
distinctBy(Vector(X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
^
但如果我帮助类型推理器,这可行:
scala> distinctBy(Vector(X(1,2),X(3,2),X(1,1),X(2,2)))((x:X) => x.i)
res1: scala.collection.immutable.Vector[X] = Vector(X(1,2), X(3,2), X(1,1), X(2,2))
scala> distinctBy[X,Int,Vector[X]](Vector(X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
res2: scala.collection.immutable.Vector[X] = Vector(X(1,2), X(3,2), X(1,1), X(2,2))
据我所知,由于函数是在第二个参数列表中给出的,因此类型推理器应该已经知道它是从X
到某个函数的函数。由于X
的成员i
类型Int
,所以第一次尝试都应该没问题。那么,我在这里错过了什么?
答案 0 :(得分:2)
这个简化版对我来说很好用:
object A {
def f1[T, R](l: List[T])(f: T=>R) = None
case class X(i: Int, j: Int)
f1(List(X(1,1),X(2,1)))(_.i)
}
如您所见,第一个参数列表中的集合具有允许第二个参数列表中的scala推理类型的T类型。
因此,您需要在示例中以某种方式构建Col和T之间的依赖关系。不确定第三个implicits参数列表是否有帮助。
UPD。 看起来很奇怪,但似乎有效:
object A {
def f1[T, R, Col[Z]](l: Col[T])(f: T => R) = None
case class X(i: Int, j: Int)
f1(List(X(1,1),X(2,1)))(_.i)
}
UPD2。 从问题中重写样本。
import scala.collection.TraversableLike
import scala.collection.generic.CanBuildFrom
import scala.collection.mutable.{Set=>MSet}
def distinctBy[T,R,Coll[Z]]
(xs: Coll[T])
(f: T => R)
(implicit ev: Coll[T] <:< TraversableLike[T,Coll[T]],
cbf: CanBuildFrom[Coll[T],T,Coll[T]]): Coll[T] = {
val builder = cbf(xs)
builder.sizeHint(xs.size)
val seen = MSet.empty[R]
xs.foreach { elem =>
if(!seen(f(elem))){
builder += elem
seen.add(f(elem))
}
}
builder.result()
}
case class X(i: Int, j: Int)
distinctBy(Vector(X(1,2),X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
distinctBy(Map("1" -> X(1,2), "2" -> X(1,2), "3" -> X(3,2)))(_._2.i)