给出以下代码:
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}时编译参数可以证明来自键的列表(或类似记录的结构)。
答案 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
更具可读性。