Scala 自定义集合无法在理解中选择正确的映射函数
Scala custom collection fails to pick correct map function in for-comprehension
我实现了一个自定义集合 class,它基本上是一个 Map
,带有隐式整数键和值,是 AnyRef
的子class。它使用 Int
键作为底层数组结构的索引。这是 class 声明签名(class 实例化是在伴随对象中完成的,因此是私有构造函数):
class ArrayMap[T >: Null <: AnyRef: ClassTag] private (private var data: Array[T]) { self =>
...
}
现在我想添加理解所需的方法。我定义了两个不同的地图函数。一个 returns 一个 List
和另一个 returns 相同的数据类型 (ArrayMap
).
def map[X](f: (Int, T) => X): List[X] = { ... }
def map[X >: Null <: AnyRef: ClassTag](f: (Int, T) => X): ArrayMap[X] = { ... }
def foreach(f: (Int, T) => Unit): Unit = { ... }
def flatMap[X >: Null <: AnyRef: ClassTag](f: (Int, T) => Iterable[(Int, X)]): ArrayMap[X] = { ... }
def filter(p: (Int, T) => Boolean): ArrayMap[T] = { ... }
没有定义隐式。单独使用时,上述功能按预期工作。问题在于理解。 For 循环要么选择第一个 map
哪个 returns List
要么抛出一个神秘的错误。以下示例产生错误:
val map = ArrayMap.empty[Integer]
map(0) = 0
map(1) = 1
map(5) = 2
map(6) = 3
map(10) = 4
val rs: ArrayMap[String] = for (e <- map) yield e._2.toString
以上代码抛出:
Error:(293, 41) missing parameter type
val rs: ArrayMap[String] = for (e <- map) yield e._2.toString
我错过了什么?
[更新]
完整的实现可作为要点 here。
问题与类型不匹配有关,您将要传递给 map
的函数定义为两个参数 (Int
& T
) 的函数 到 X
。而在您的理解中,您将其视为一个参数的函数 (a tuple (Int, T)
) to X
.
最简单的解决方案是重新定义您的 map
函数签名。 例如
import scala.reflect.ClassTag
class ArrayMap[T >: Null <: AnyRef: ClassTag] (val data: Array[T]) {
// Note the double parenthesis (()).
def map[X >: Null <: AnyRef: ClassTag](f: ((Int, T)) => X): ArrayMap[X] = ???
def withFilter(p: ((Int, T)) => Boolean): ArrayMap[T] = ???
}
有了这个定义,你可以做出类似
的东西
val map: ArrayMap[java.lang.Integer] = new ArrayMap(Array(1, 2, 3))
// Note I use lazy val to avoid the NotImplementedException.
lazy val rs1: ArrayMap[String] = map.map(tuple => tuple._2.toString)
lazy val rs2: ArrayMap[String] = map.map { case (_, v) => v.toString }
lazy val rs3: ArrayMap[String] = for {
tuple <- map
} yield tuple._2.toString
lazy val rs4: ArrayMap[String] = for {
(_, v) <- map
} yield v.toString
参见 Scala Map 中 map
的 full signature 作为参考。
我实现了一个自定义集合 class,它基本上是一个 Map
,带有隐式整数键和值,是 AnyRef
的子class。它使用 Int
键作为底层数组结构的索引。这是 class 声明签名(class 实例化是在伴随对象中完成的,因此是私有构造函数):
class ArrayMap[T >: Null <: AnyRef: ClassTag] private (private var data: Array[T]) { self =>
...
}
现在我想添加理解所需的方法。我定义了两个不同的地图函数。一个 returns 一个 List
和另一个 returns 相同的数据类型 (ArrayMap
).
def map[X](f: (Int, T) => X): List[X] = { ... }
def map[X >: Null <: AnyRef: ClassTag](f: (Int, T) => X): ArrayMap[X] = { ... }
def foreach(f: (Int, T) => Unit): Unit = { ... }
def flatMap[X >: Null <: AnyRef: ClassTag](f: (Int, T) => Iterable[(Int, X)]): ArrayMap[X] = { ... }
def filter(p: (Int, T) => Boolean): ArrayMap[T] = { ... }
没有定义隐式。单独使用时,上述功能按预期工作。问题在于理解。 For 循环要么选择第一个 map
哪个 returns List
要么抛出一个神秘的错误。以下示例产生错误:
val map = ArrayMap.empty[Integer]
map(0) = 0
map(1) = 1
map(5) = 2
map(6) = 3
map(10) = 4
val rs: ArrayMap[String] = for (e <- map) yield e._2.toString
以上代码抛出:
Error:(293, 41) missing parameter type
val rs: ArrayMap[String] = for (e <- map) yield e._2.toString
我错过了什么?
[更新]
完整的实现可作为要点 here。
问题与类型不匹配有关,您将要传递给 map
的函数定义为两个参数 (Int
& T
) 的函数 到 X
。而在您的理解中,您将其视为一个参数的函数 (a tuple (Int, T)
) to X
.
最简单的解决方案是重新定义您的 map
函数签名。 例如
import scala.reflect.ClassTag
class ArrayMap[T >: Null <: AnyRef: ClassTag] (val data: Array[T]) {
// Note the double parenthesis (()).
def map[X >: Null <: AnyRef: ClassTag](f: ((Int, T)) => X): ArrayMap[X] = ???
def withFilter(p: ((Int, T)) => Boolean): ArrayMap[T] = ???
}
有了这个定义,你可以做出类似
的东西val map: ArrayMap[java.lang.Integer] = new ArrayMap(Array(1, 2, 3))
// Note I use lazy val to avoid the NotImplementedException.
lazy val rs1: ArrayMap[String] = map.map(tuple => tuple._2.toString)
lazy val rs2: ArrayMap[String] = map.map { case (_, v) => v.toString }
lazy val rs3: ArrayMap[String] = for {
tuple <- map
} yield tuple._2.toString
lazy val rs4: ArrayMap[String] = for {
(_, v) <- map
} yield v.toString
参见 Scala Map 中 map
的 full signature 作为参考。