Scala 声明类型不同时的不同行为(Set与TreeSet)

Scala 声明类型不同时的不同行为(Set与TreeSet),scala,for-loop,Scala,For Loop,当我想使用for循环时 def genSet:Set[Int] = { TreeSet(5, 4, 3, 2, 1) } 将发电机组的sig更改为返回分拣数据集 for (i <- genSet) { val x = i + 1 println(x) } 这可能是某种错误。我希望你的代码也能工作 我认为map是罪魁祸首。这会导致相同的行为: def genSet:SortedSet[Int] = { TreeSet(5, 4, 3, 2, 1) } for(iSet不

当我想使用for循环时

def genSet:Set[Int] = {
  TreeSet(5, 4, 3, 2, 1)
}

发电机组的sig更改为返回
分拣数据集

for (i <- genSet) {
  val x = i + 1
  println(x)
}
这可能是某种错误。我希望你的代码也能工作

我认为
map
是罪魁祸首。这会导致相同的行为:

def genSet:SortedSet[Int] = {
  TreeSet(5, 4, 3, 2, 1)
}

for(i
Set
不保证排序。即使基础类是
TreeSet
,如果预期结果是
Set
,您将在执行第一次转换时取消排序

如果您想订购,请不要使用
Set
。我建议,比如说,
SortedSet

为什么
map
版本最终未排序
map
方法(使用我们将调用的函数
func
调用)采用隐式的
CanBuildFrom
参数,该参数除了
func
返回的类型之外,还考虑调用
map
的集合类型,以选择适当的返回类型。这用于使
map.map[Int]
BitSet.map[String]
执行正确的操作(返回通用列表)而
Map.Map[(String,Int)]
BitSet.Map[Int]
也分别返回一个
Map
和一个
BitSet

编译时选择了
CanBuildFrom
,因此必须根据调用
map
的集合的静态类型(编译器在编译时知道的类型)来选择它
set
的静态类型为
TreeSet
,而
diffset
的静态类型为
set
。两者(运行时)的动态类型均为
TreeSet

set
(a
TreeSet
)上调用
map
,编译器选择
不可变。this.SortedSet.canBuildFrom[Int](math.this.Ordering.Int)
作为
canBuildFrom

diffset
(a
Set
)上调用
map
,编译器选择
不可变。this.Set.canBuildFrom[Int]
作为
canBuildFrom

为什么
版本的
未排序
环路

for (i <- genSet.map(_ + 1)) { println(i) }
desugared版本包括一个
map
函数,该函数将使用未排序的
CanBuildFrom
,如上所述

另一方面,循环

genSet.map(((i) => {
              val x = i.$plus(1);
              scala.Tuple2(i, x)
            })).foreach(((x$1) => x$1: @scala.unchecked match {
              case scala.Tuple2((i @ _), (x @ _)) => println(x)
            }))
因为没有返回新集合,所以根本不使用
CanBuildFrom

您可以执行以下操作:

genSet.foreach(((i) => {
              val x = i.$plus(1);
              println(x)
            }))

scala>for(i)调用“错误”映射:scala>TreeSet(1,2,3,4,5)。map(identity)res1:scala.collection.immutable.SortedSet[Int]=TreeSet(4,5,1,2,3)scala>(TreeSet(1,2,3,4,5):Set[Int])。map(identity)res2:scala.collection.immutable.Set[Int]=Set(4,5,1,2,3)在我看来是这样的。2.8的大规模重构狂潮应该可以减少这样的问题……2.8的大规模重构狂潮应该可以解决其他更常见的问题(参见我对这个问题的回答)通过引入
CanBuildFrom
来决定调用
map
应该返回什么类型。引入
CanBuildFrom
产生了您看到的问题。这可能是邮件列表的问题:scala-user@listes.epfl.chI认为依赖实例化对象恰好是TreeSet
。如果您需要排序,那么您希望静态类型表示集合将被排序。如果您注意到来自第三方库的函数
def:set[T]
碰巧返回
TreeSet[T]
,我相信您不愿意在代码中依赖它,因为如果库设计器决定更改实例化的类型,它将崩溃。在调用您自己的代码时使用相同的规则可能是个好主意。这不违反多态性吗?使用接口来保证发生什么,但实例的行为应该d不随使用环境的不同而变化。@sblundy:有不同类型的多态性。继承和函数重写是一种多态性。泛型是另一种多态性。正是后一种多态性导致了问题。@Ken Bloom:根据您的回答,问题似乎是隐式的。它们使方法能够根据其上下文更改其行为。有时这是一个功能,有时不是。@sblundy:这是正确的。这是隐含的。我在上一条评论中做了一个愚蠢的大脑屁。@sblundy不是,它不违反多态性——这是由类型参数决定的。具体地说,
Set
extends
SetLike[a,Set[a]]
,它是
SetLike
的第二个参数,用于确定
map
和类似方法返回的类型的下界。
SortedSet
间接扩展了
SetLike[A,This]
,其中
This
定义为
SortedSet[A]
,这降低了界限。从类型界限跳到结果类型的最后一步是通过隐式完成的。非常有趣。在我看来,该方法的行为因此取决于它被分配到的变量的类型,隐式是发生这种情况的手段。可以说,这只是一个简单的drawb确认API的设计,但这种行为是意外的。@sblundy:您希望
SortedSet(1,2,3).map(x=>10-x)
中的数字顺序是什么?请注意,编译器对转换函数的了解不够,无法将
SortedSet(7,6,5)
作为一个选项。那么
SortedSet(2,3,4,5).map呢(x=>x/2)
(使用整数除法)?@sblundy:根据他试图对
SortedSet
中元素的顺序做什么,他实际上想在调用map之前调用
toSeq
(如果该点以与映射值在集合中的顺序相对应的顺序遍历映射值),或者他可能想
for (i <- genSet; x = i + 1) {
  println(x)
}
genSet.map(((i) => {
              val x = i.$plus(1);
              scala.Tuple2(i, x)
            })).foreach(((x$1) => x$1: @scala.unchecked match {
              case scala.Tuple2((i @ _), (x @ _)) => println(x)
            }))
for (i <- genSet) {
  val x = i + 1
  println(x)
}
genSet.foreach(((i) => {
              val x = i.$plus(1);
              println(x)
            }))
scala> for (i <-genSet.view; x = i + 1) println(x)
2
3
4
5
6