fri*_*ron 7 collections functional-programming scala sortedmap higher-order-functions
我在使用Scala的SortedMap [A,B]时遇到了一些未经授权的陌生感.如果我将SortedMap [A,B]"a"的引用声明为Map [A,B]类型,那么对"a"的映射操作将产生一个非有序映射实现.
例:
import scala.collection.immutable._
object Test extends App {
val a: Map[String, String] = SortedMap[String, String]("a" -> "s", "b" -> "t", "c" -> "u", "d" -> "v", "e" -> "w", "f" -> "x")
println(a.getClass+": "+a)
val b = a map {x => x} // identity
println(b.getClass+": "+b)
}
Run Code Online (Sandbox Code Playgroud)
以上的输出是:
class scala.collection.immutable.TreeMap:Map(a - > s,b - > t,c - > u,d - > v,e - > w,f - > x)
class scala.collection.immutable.HashMap $ HashTrieMap:Map(e - > w,f - > x,a - > s,b - > t,c - > u,d - > v)
身份转换之前和之后的键/值对的顺序不相同.
奇怪的是,从"a"中删除类型声明会使这个问题消失.这在玩具示例中很好,但是使得SortedMap [A,B]无法传递给期望Map [A,B]参数的方法.
一般来说,我希望更高阶的函数,如"map"和"filter",不会改变它们应用的集合的基本属性.
有谁知道为什么"地图"表现得像这样?
与大多数集合方法一样,该map方法不是专门为SortedMap. 它是在更高级别的类(TraversableLike)上定义的,并使用“构建器”将映射结果转换为正确的返回类型。
那么它如何决定“正确”的返回类型是什么?好吧,它试图返回它最初的返回类型。当您告诉 Scala 您有 aMap[String,String]并要求它时map,构建器必须弄清楚如何“构建”返回的类型。由于您告诉 Scala 输入是 a ,因此构建器决定为您Map[String,String]构建 a 。Map[String,String]建造者不知道你想要一个SortedMap,所以它不会给你一个。
当您省略类型Map[String,String]注释时它起作用的原因是 Scala 推断 的类型a是SortedMap[String,String]。因此,当您调用 时map,您是在 a 上调用它SortedMap,并且构建器知道构造 aSortedMap来返回。
至于您断言方法不应改变“基本属性”,我认为您从错误的角度看待它。这些方法将始终返回一个符合您指定类型的对象。它是定义构建器行为的类型,而不是底层实现。当您这样思考时,类型就形成了方法应如何表现的契约。
为什么这是首选行为?让我们看一个具体的例子。假设我们有一个SortedMap[Int,String]
val sortedMap = SortedMap[Int, String](1 -> "s", 2 -> "t", 3 -> "u", 4 -> "v")
Run Code Online (Sandbox Code Playgroud)
如果我用map修改键的函数来解决它,那么当它们的键发生冲突时,我就会面临丢失元素的风险:
scala> sortedMap.map { case (k, v) => (k / 2, v) }
res3: SortedMap[Int,String] = Map(0 -> s, 1 -> u, 2 -> v)
Run Code Online (Sandbox Code Playgroud)
但是嘿,没关系。Map毕竟它是 a ,而且我知道它是 a Map,所以我应该预料到这种行为。
现在假设我们有一个接受Iterableof 对的函数:
def f(iterable: Iterable[(Int, String)]) =
iterable.map { case (k, v) => (k / 2, v) }
Run Code Online (Sandbox Code Playgroud)
由于该函数与 s 无关,因此如果该函数的结果的元素少于输入的元素,Map那将是非常令人惊讶的。毕竟,mapon aIterable应该生成每个元素的映射版本。但 aMap 是一个Iterable对,所以我们可以将它传递到这个函数中。那么当我们这样做时,Scala 中会发生什么呢?
scala> f(sortedMap)
res4: Iterable[(Int, String)] = List((0,s), (1,t), (1,u), (2,v))
Run Code Online (Sandbox Code Playgroud)
看那个!没有元素丢失!换句话说,Scala 不会因为违反我们对mapon anIterable应该如何工作的期望而让我们感到惊讶。如果构建器尝试SortedMap根据输入是 a 的事实来生成 a SortedMap,那么我们的函数f将会产生令人惊讶的结果,这将是糟糕的。
所以这个故事的寓意是:使用类型告诉集合框架如何处理数据。如果您希望代码能够预期地图已排序,那么您应该将其键入为SortedMap.
| 归档时间: |
|
| 查看次数: |
1162 次 |
| 最近记录: |