Scala SortedSet映射不总是在结果中保留元素顺序?
给定以下Scala 2.9.2代码: 使用非工作示例更新Scala SortedSet映射不总是在结果中保留元素顺序?,scala,Scala,给定以下Scala 2.9.2代码: 使用非工作示例更新 import collection.immutable.SortedSet case class Bar(s: String) trait Foo { val stuff: SortedSet[String] def makeBars(bs: Map[String, String]) = stuff.map(k => Bar(bs.getOrElse(k, "-"))).toList } case class
import collection.immutable.SortedSet
case class Bar(s: String)
trait Foo {
val stuff: SortedSet[String]
def makeBars(bs: Map[String, String])
= stuff.map(k => Bar(bs.getOrElse(k, "-"))).toList
}
case class Bazz(rawStuff: List[String]) extends Foo {
val stuff = SortedSet(rawStuff: _*)
}
// test it out....
val b = Bazz(List("A","B","C"))
b.makeBars(Map("A"->"1","B"->"2","C"->"3"))
// List[Bar] = List(Bar(1), Bar(2), Bar(3))
// Looks good?
// Make a really big list not in order. This is why we pass it to a SortedSet...
val data = Stream.continually(util.Random.shuffle(List("A","B","C","D","E","F"))).take(100).toList
val b2 = Bazz(data.flatten)
// And how about a sparse map...?
val bs = util.Random.shuffle(Map("A" -> "1", "B" -> "2", "E" -> "5").toList).toMap
b2.makeBars(bs)
// res24: List[Bar] = List(Bar(1), Bar(2), Bar(-), Bar(5))
我发现,在某些情况下,扩展Foo
类的makebar
方法不会返回排序列表。事实上,列表顺序并不反映SortedSet
在上面的代码中,Scala不总是将
SortedSet
映射到列表,元素按SortedSet
排序,我缺少什么呢?隐式解析让您大吃一惊
map
方法需要一个与目标集合类型(在简单情况下,与源集合类型相同)和映射器函数的返回类型兼容的CanBuildFrom
实例
在SortedSet
的特定情况下,其隐式可以从构建,要求排序[A]
(其中A是映射器函数的返回类型)可用。当map函数返回编译器已经知道如何查找排序的内容时,您就可以:
scala> val ss = collection.immutable.SortedSet(10,9,8,7,6,5,4,3,2,1)
ss: scala.collection.immutable.SortedSet[Int] = TreeSet(1, 2, 3, 4, 5,
6, 7, 8, 9, 10)
scala> val result1 = ss.map(_ * 2)
result1: scala.collection.immutable.SortedSet[Int] = TreeSet(2, 4, 6, 8, 10,
12, 14, 16, 18, 20)
// still sorted because Ordering[Int] is readily available
scala> val result2 = ss.map(_ + " is a number")
result2: scala.collection.immutable.SortedSet[String] = TreeSet(1 is a number,
10 is a number,
2 is a number,
3 is a number,
4 is a number,
5 is a number,
6 is a number,
7 is a number,
8 is a number,
9 is a number)
// The default Ordering[String] is an "asciibetical" sort,
// so 10 comes between 1 and 2. :)
但是,当映射器函数返回的类型不知道其顺序时,SortedSet
上的隐式参数不匹配(特别是,找不到其隐式参数的值),因此编译器会“向上”查找兼容的CanBuildFrom
并从Set
中查找泛型参数
scala> case class Foo(i: Int)
defined class Foo
scala> val result3 = ss.map(Foo(_))
result3: scala.collection.immutable.Set[Foo] = Set(Foo(10), Foo(4), Foo(6), Foo(7), Foo(1), Foo(3), Foo(5), Foo(8), Foo(9), Foo(2))
// The default Set is a hash set, therefore ordering is not preserved
当然,您可以通过简单地提供一个Ordering[Foo]
实例来解决这个问题,该实例可以实现您所期望的任何功能:
scala> implicit val fooIsOrdered: Ordering[Foo] = Ordering.by(_.i)
fooIsOrdered: Ordering[Foo] = scala.math.Ordering$$anon$9@7512dbf2
scala> val result4 = ss.map(Foo(_))
result4: scala.collection.immutable.SortedSet[Foo] = TreeSet(Foo(1), Foo(2),
Foo(3), Foo(4), Foo(5),
Foo(6), Foo(7), Foo(8),
Foo(9), Foo(10))
// And we're back!
最后,请注意,玩具示例通常不会出现问题,因为Scala集合库具有针对小型应用程序的特殊实现(n您可能在假设SortedSet在Java中的功能。您需要指定元素的顺序。请参见您需要提供一个实际不起作用的示例。同意。这将需要一些时间,因为我必须清理一些专有代码。更新。我遗漏了map函数实际上正在包装case类“Bar”中的stuff
SortedSet元素再次更新,这一次显示了makeBars
函数中的更多缺陷。OP会发现他的错误(未定义Bar的顺序)如果他为他的makebar
方法提供了一个类型注释。@Alex Cruise哇,那太棒了!还有一个原因让我不总是喜欢Scala集合的继承结构。事实上,我对map
做了一个错误的假设,因为我已经习惯于关闭map函数总是返回seq
-没有CanBuildFrom
。