来自Java背景,我正在学习Scala,以下内容使我感到非常困惑。为什么在这两个(非常相似但不同)的构造中返回的类型不同,它们仅在构建源集合的方式上有所不同-
val seq1: IndexedSeq[Int] = for (i <- 1 to 10) yield i
与
val seq2: Array[Int] = for (i <- Array(1, 2, 3)) yield i
请为我指出正确的文献,以便在这里我能理解核心基础。
通常,有两种不同样式的收集操作库:
类型保持收集操作尽量准确地保存类型操作,例如filter
,take
,drop
等,仅利用现有未经修改的元素。对于类似的操作map
,它将尝试查找仍可以保存结果的最接近的超类型。例如,将IntSet
具有从Int
到函数的映射到String
显然不会导致IntSet
,而只会导致Set
。将映射IntSet
到Boolean
可以在中表示BitSet
,但我知道没有一个足够聪明的集合框架可以真正做到这一点。
通用/同类收集操作始终返回相同的类型。通常,选择此类型非常通用,以适应最广泛的用例。例如,在.NET中,集合操作返回IEnumerable
;在Java中,它们返回Stream
s;在C ++中,它们返回迭代器;在Ruby中,它们返回数组。
直到最近,才有可能通过复制所有类型的所有操作来实现类型保留的收集操作。例如,Smalltalk集合框架是保留类型的,它通过让每个单个集合类重新实现每个单个集合操作来做到这一点。这导致大量重复的代码,并且是维护的噩梦。(巧合的是,发明了许多新的面向对象的抽象方法,并就其如何应用于Smalltalk集合框架撰写了第一篇论文。有关示例,请参见特性:行为的可组合单位。)
据我所知,Scala 2.8集合框架的重新设计(另请参见SO上的此答案)是有人第一次设法创建保留类型的集合操作,同时又将复制减少到最小程度(尽管没有消除)。但是,Scala 2.8集合框架被广泛批评为过于复杂,并且在过去十年中需要不断地工作。实际上,它实际上也导致了Scala文档系统的完全重新设计,只是为了能够隐藏类型保留操作所需的非常复杂的类型签名。但是,这还不够,因此collection框架被完全丢弃并重新设计了再次在Scala 2.13中。(此重新设计花了几年时间。)
因此,对您的问题的简单答案是:Scala尝试尽可能保留集合的类型。
在第二个情况下,集合的类型Array
,当你map
过了Array
,你回来了Array
。
在第一种情况下,集合的类型为Range
。现在,aRange
实际上没有元素。它只有一个起点,一个终点和一个步骤,并且在您迭代时按需生成元素。因此,Range
用新元素生产新产品并不容易。该map
功能基本上需要能够对映射功能进行“逆向工程”,以弄清楚新的起点和终点以及步骤应该是什么。(这相当于解决停止问题,换句话说,这是不可能的。)如果执行以下操作,该怎么办:
val seq1: IndexedSeq[Int] = for (i <- 1 to 10) yield scala.util.Random.nextInt(i)
在这里,甚至没有明确定义的步骤,因此实际上不可能构建一个Range
执行此操作的步骤。
因此,很明显,映射到aRange
不能返回a Range
。因此,它做的第二件事是:它返回可以包含映射值的最精确的超类型Range
。在这种情况下,恰好是IndexedSeq
。
在保留类型的集合操作方面,我们遇到了麻烦,我们认为这是某些操作合同的一部分。例如,大多数人会争辩说,集合的基数应该在下不变map
,换句话说,map
应该将每个元素映射到一个新的元素,因此map
永远不要更改集合的大小。但是,这段代码呢:
Set(1, 2, 3).map { _ % 2 == 0 }
//=> Set(true, false)
在这里,您从中获得了元素较少的集合,该集合map
仅应转换元素,而不能删除它们。但是,由于我们决定要保留类型的集合,并且aSet
不能有重复的值,所以两个false
值实际上是相同的值,因此集合中只有一个。
[可以争论的是,这实际上仅表明Set
s不是集合,不应将其视为集合。Set
s是谓词(“此元素是成员吗?”)而不是集合(“给我所有元素!”)
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句