当映射到相同类型时,Scala的map()行为是否有所不同?

| 在Scala Collections框架中,我认为使用
map()
时有些行为违反直觉。 我们可以区分(不可变)集合的两种转换。那些实现调用“ 1”来重新生成结果集合的对象,而那些通过隐式“ 2”来获取构建器的对象。 第一类包含所有转换,其中所包含元素的类型不变。例如,它们是
filter
partition
drop
take
span
,等等。这些转换可以自由调用
newBuilder
,并重新创建与它们所调用的集合类型相同的集合类型,无论多么具体:过滤
List[Int]
总是可以返回a9ѭ;过滤
BitSet
(或本文在collections框架的体系结构中介绍的
RNA
示例结构)始终可以返回另一个
BitSet
(或
RNA
)。让我们称它们为过滤转换。 第二类转换需要ѭ2才能更加灵活,因为所包含元素的类型可能会更改,因此,集合本身的类型可能无法重用:
BitSet
不能包含
String
RNA
仅包含
Base
s。这样的转换的示例是
map
flatMap
collect
scanLeft
++
,等等。让我们将它们称为映射转换。 现在,这里是要讨论的主要问题。无论集合的静态类型是什么,所有过滤转换都将返回相同的集合类型,而映射操作返回的集合类型可以根据静态类型而有所不同。
scala> import collection.immutable.TreeSet
import collection.immutable.TreeSet

scala> val treeset = TreeSet(1,2,3,4,5) // static type == dynamic type
treeset: scala.collection.immutable.TreeSet[Int] = TreeSet(1, 2, 3, 4, 5)

scala> val set: Set[Int] = TreeSet(1,2,3,4,5) // static type != dynamic type
set: Set[Int] = TreeSet(1, 2, 3, 4, 5)

scala> treeset.filter(_ % 2 == 0)
res0: scala.collection.immutable.TreeSet[Int] = TreeSet(2, 4) // fine, a TreeSet again

scala> set.filter(_ % 2 == 0)    
res1: scala.collection.immutable.Set[Int] = TreeSet(2, 4) // fine

scala> treeset.map(_ + 1)        
res2: scala.collection.immutable.SortedSet[Int] = TreeSet(2, 3, 4, 5, 6) // still fine

scala> set.map(_ + 1)    
res3: scala.collection.immutable.Set[Int] = Set(4, 5, 6, 2, 3) // uh?!
现在,我明白了为什么这样做会如此。它在那里那里解释。简而言之:隐式the2 on是基于静态类型插入的,并且取决于其
def apply(from: Coll)
方法的实现,可能会或可能不会重新创建相同的集合类型。 现在我唯一要说的是,当我们知道我们正在使用映射操作生成具有相同元素类型的集合(编译器可以静态确定)时,我们可以模仿过滤转换的工作方式并使用该集合的本机建造者。当映射到ѭ29时,我们可以重用ѭ11,以相同的顺序创建新的ѭ30,等等。 那么我们将避免以下情况
for (i <- set) {
  val x = i + 1
  println(x)
}
不会以与打印顺序相同的顺序打印
TreeSet
的递增元素
for (i <- set; x = i + 1)
  println(x)
所以: 您是否认为按上述说明更改映射转换的行为是一个好主意? 我完全忽略了哪些不可避免的警告? 如何实施? 我正在考虑类似
implicit sameTypeEvidence: A =:= B
的参数,可能具有默认值
null
(或更确切地说是
implicit canReuseCalleeBuilderEvidence: B <:< A = null
),该参数可在运行时用于向
CanBuildFrom
提供更多信息,进而可用于确定构建器的类型回来。     
已邀请:
我再次看了一下,我认为您的问题不是由Scala集合的特定缺陷引起的,而是由30英镑的缺失生成器引起的。因为以下确实可以正常工作:
val list = List(1,2,3,4,5)
val seq1: Seq[Int] = list
seq1.map( _ + 1 ) // yields List

val vector = Vector(1,2,3,4,5)
val seq2: Seq[Int] = vector
seq2.map( _ + 1 ) // yields Vector
因此,原因是ѭ30缺少一个专门的伴随对象/生成器:
seq1.companion.newBuilder[Int]    // ListBuffer
seq2.companion.newBuilder[Int]    // VectorBuilder
treeset.companion.newBuilder[Int] // Set (oops!)
因此,我的猜测是,如果您为ѭ12班的学生提供适当的陪伴,您可能会发现
map
filter
都能按您的意愿工作...?     

要回复问题请先登录注册