地图的{type}类型密钥

时间:2016-05-11 16:56:18

标签: scala shapeless type-level-computation

给出以下代码:

val m: Map[String, Int] = .. // fetch from somewhere
val keys: List[String] = m.keys.toList
val keysSubset: List[String] = ... // choose random keys

我们可以定义以下方法:

def sumValues(m: Map[String, Int], ks: List[String]): Int =
  ks.map(m).sum

并将其称为:

sumValues(m, keysSubset)

然而,sumValues的问题在于,如果ks恰好在地图上没有密钥,代码仍会编译但在运行时抛出异常。例如:

// assume m = Map("two" -> 2, "three" -> 3)
sumValues(m, 1 :: Nil)

我想要的是sumValues的定义,使得ks参数在编译时应保证仅包含map上存在的密钥。因此,我的猜测是现有的sumValues类型签名需要接受某种形式的隐式证据,即ks参数以某种方式从地图的键列表中派生出来。

我不仅限于scala Map,因为任何类似记录的结构都可以。然而,地图结构不具有硬编码值,而是作为参数派生/传递的内容。

注意:我在总结这些值之后并不是真的,但更多的是找出sumValues的类型签名,它的调用只能在{{1}时编译参数可以证明来自键的列表(或类似记录的结构)。

1 个答案:

答案 0 :(得分:1)

另一种解决方案可能是仅映射交叉点(即:m键和ks之间)。

例如:

scala> def sumValues(m: Map[String, Int], ks: List[String]): Int = {
 |   m.keys.filter(ks.contains).map(m).sum
 | }
sumValues: (m: Map[String,Int], ks: List[String])Int

scala> val map = Map("hello" -> 5)
map: scala.collection.immutable.Map[String,Int] = Map(hello -> 5)

scala> sumValues(map, List("hello", "world"))
res1: Int = 5

我认为这个解决方案比提供默认值更好,因为更通用(即:你不仅可以将它用于总和)。但是,我猜这个解决方案在性能方面效果较差,因为交叉点。

编辑:正如@jwvh在下面的消息中指出的那样,ks.intersect(m.keys.toSeq).map(m).sum在我看来比m.keys.filter(ks.contains).map(m).sum更具可读性。