在 Scala 中推断泛型类型参数
Infering generic type parameters in Scala
嗨,我一直在尝试统一嵌套地图的集合。
所以我想实现一个带有签名的方法:
def unifyMaps(seq: Seq[Map[String, Map[WordType, Int]]]): Map[String, Map[WordType, Int]]
(WordType 是一个 Java 枚举。)第一种方法是进行手动映射合并。
def unifyMapsManually(seq: IndexedSeq[Map[String, Map[WordType, Int]]]): Map[String, Map[WordType, Int]] = {
seq reduce { (acc, newMap) =>
acc ++ newMap.map { case (k, v) =>
val nestedMap = acc.getOrElse(k, Map.empty)
k -> (nestedMap ++ v.map { case (k2, v2) => k2 -> (nestedMap.getOrElse(k2, 0) + v2) })
}
}
}
它有效,但我在这里所做的是递归地应用完全相同的模式,所以我想我会制作一个递归通用版本。
第二种方法:
def unifyTwoMapsRecursively(m1: Map[String, Map[WordType, Int]], m2: Map[String, Map[WordType, Int]]): Map[String, Map[WordType, Int]] = {
def unifyTwoMaps[K, V](nestedMapOps: (V, (V, V) => V))(m1: Map[K, V], m2: Map[K, V]): Map[K, V] = {
nestedMapOps match {
case (zero, add) =>
m1 ++ m2.map { case (k, v) => k -> add(m1.getOrElse(k, zero), v) }
}
}
val intOps = (0, (a: Int, b: Int) => a + b)
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps) _)
unifyTwoMaps(mapOps)(m1, m2)
}
但它失败了:
Error:(90, 18) type mismatch;
found : (scala.collection.immutable.Map[pjn.wierzba.DictionaryCLP.WordType,Int], (Map[Nothing,Int], Map[Nothing,Int]) => Map[Nothing,Int])
required: (scala.collection.immutable.Map[_ <: pjn.wierzba.DictionaryCLP.WordType, Int], (scala.collection.immutable.Map[_ <: pjn.wierzba.DictionaryCLP.WordType, Int], scala.collection.immutable.Map[_ <: pjn.wierzba.DictionaryCLP.WordType, Int]) => scala.collection.immutable.Map[_ <: pjn.wierzba.DictionaryCLP.WordType, Int])
unifyTwoMaps(mapOps)(m1, m2)
^
好吧,我不知道地图键的上限,但柯里化函数显然没有正确推断。我在 intOps
中遇到了类似的错误,所以我尝试提供确切的类型:
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps)(_: Map[String, Map[WordType, Int]], _: Map[String, Map[WordType, Int]]))
但是这次失败了:
Error:(89, 67) type mismatch;
found : Map[String,Map[pjn.wierzba.DictionaryCLP.WordType,Int]]
required: Map[?,Int]
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps)(_: Map[String, Map[WordType, Int]], _: Map[String, Map[WordType, Int]]))
^
这次我完全不知道接下来要尝试什么才能让它正常工作。
编辑:我已经找到了问题的解决方案,但我仍然想知道为什么在此代码段中出现类型不匹配错误:
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps) _)
根据 this answer,scala 类型推断适用于每个参数列表 - 这正是我在这里为柯里化目的所做的。我的 unifyTwoMaps
函数采用两个参数列表,我试图仅推断第二个。
泛型递归求解
好的,所以在花了一个早上之后我终于明白我一直在提供错误的确切类型。
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps)(_: Map[String, Map[WordType, Int]], _: Map[String, Map[WordType, Int]]))
应该是
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps)(_: Map[WordType, Int], _: Map[WordType, Int]))
因为我需要传递Map的V
、Map[WordType, Int]
的类型,而不是整个外部地图的类型。现在可以使用了!
解决嵌套map合并底层问题
好吧,对映射的抽象 V
zero
和 add
应该敲响警钟,我一直在重新发明 Monoid。所以我想我会尝试 this answer.
中的 Scalaz |+|
半群运算符解决方案
import scalaz.Scalaz._
def unifyMapsWithScalaz(seq: Seq[Map[String, Map[WordType, Int]]]): Map[String, Map[WordType, Int]] = {
seq reduce (_ |+| _)
}
而且有效!
有趣的是,我在尝试我的解决方案之前已经看到了 post,但我认为我不确定它是否适用于嵌套数据结构,尤其是当我的地图键为 Java枚举。我想我必须提供一些扩展 Semigroups 类型类的自定义实现。
但正如我在重新发明轮子的实施过程中所证明的那样,枚举只需要作为传递的类型和映射键,而且效果很好。 Scalaz 干得好!
好吧,这会成为一个很好的博客 post 事实上..
编辑: 但我仍然不明白为什么我首先遇到这种类型推断问题,我已经更新了问题。
嗨,我一直在尝试统一嵌套地图的集合。 所以我想实现一个带有签名的方法:
def unifyMaps(seq: Seq[Map[String, Map[WordType, Int]]]): Map[String, Map[WordType, Int]]
(WordType 是一个 Java 枚举。)第一种方法是进行手动映射合并。
def unifyMapsManually(seq: IndexedSeq[Map[String, Map[WordType, Int]]]): Map[String, Map[WordType, Int]] = {
seq reduce { (acc, newMap) =>
acc ++ newMap.map { case (k, v) =>
val nestedMap = acc.getOrElse(k, Map.empty)
k -> (nestedMap ++ v.map { case (k2, v2) => k2 -> (nestedMap.getOrElse(k2, 0) + v2) })
}
}
}
它有效,但我在这里所做的是递归地应用完全相同的模式,所以我想我会制作一个递归通用版本。 第二种方法:
def unifyTwoMapsRecursively(m1: Map[String, Map[WordType, Int]], m2: Map[String, Map[WordType, Int]]): Map[String, Map[WordType, Int]] = {
def unifyTwoMaps[K, V](nestedMapOps: (V, (V, V) => V))(m1: Map[K, V], m2: Map[K, V]): Map[K, V] = {
nestedMapOps match {
case (zero, add) =>
m1 ++ m2.map { case (k, v) => k -> add(m1.getOrElse(k, zero), v) }
}
}
val intOps = (0, (a: Int, b: Int) => a + b)
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps) _)
unifyTwoMaps(mapOps)(m1, m2)
}
但它失败了:
Error:(90, 18) type mismatch;
found : (scala.collection.immutable.Map[pjn.wierzba.DictionaryCLP.WordType,Int], (Map[Nothing,Int], Map[Nothing,Int]) => Map[Nothing,Int])
required: (scala.collection.immutable.Map[_ <: pjn.wierzba.DictionaryCLP.WordType, Int], (scala.collection.immutable.Map[_ <: pjn.wierzba.DictionaryCLP.WordType, Int], scala.collection.immutable.Map[_ <: pjn.wierzba.DictionaryCLP.WordType, Int]) => scala.collection.immutable.Map[_ <: pjn.wierzba.DictionaryCLP.WordType, Int])
unifyTwoMaps(mapOps)(m1, m2)
^
好吧,我不知道地图键的上限,但柯里化函数显然没有正确推断。我在 intOps
中遇到了类似的错误,所以我尝试提供确切的类型:
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps)(_: Map[String, Map[WordType, Int]], _: Map[String, Map[WordType, Int]]))
但是这次失败了:
Error:(89, 67) type mismatch;
found : Map[String,Map[pjn.wierzba.DictionaryCLP.WordType,Int]]
required: Map[?,Int]
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps)(_: Map[String, Map[WordType, Int]], _: Map[String, Map[WordType, Int]]))
^
这次我完全不知道接下来要尝试什么才能让它正常工作。
编辑:我已经找到了问题的解决方案,但我仍然想知道为什么在此代码段中出现类型不匹配错误:
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps) _)
根据 this answer,scala 类型推断适用于每个参数列表 - 这正是我在这里为柯里化目的所做的。我的 unifyTwoMaps
函数采用两个参数列表,我试图仅推断第二个。
泛型递归求解
好的,所以在花了一个早上之后我终于明白我一直在提供错误的确切类型。
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps)(_: Map[String, Map[WordType, Int]], _: Map[String, Map[WordType, Int]]))
应该是
val mapOps = (Map.empty[WordType, Int], unifyTwoMaps(intOps)(_: Map[WordType, Int], _: Map[WordType, Int]))
因为我需要传递Map的V
、Map[WordType, Int]
的类型,而不是整个外部地图的类型。现在可以使用了!
解决嵌套map合并底层问题
好吧,对映射的抽象 V
zero
和 add
应该敲响警钟,我一直在重新发明 Monoid。所以我想我会尝试 this answer.
|+|
半群运算符解决方案
import scalaz.Scalaz._
def unifyMapsWithScalaz(seq: Seq[Map[String, Map[WordType, Int]]]): Map[String, Map[WordType, Int]] = {
seq reduce (_ |+| _)
}
而且有效!
有趣的是,我在尝试我的解决方案之前已经看到了 post,但我认为我不确定它是否适用于嵌套数据结构,尤其是当我的地图键为 Java枚举。我想我必须提供一些扩展 Semigroups 类型类的自定义实现。
但正如我在重新发明轮子的实施过程中所证明的那样,枚举只需要作为传递的类型和映射键,而且效果很好。 Scalaz 干得好!
好吧,这会成为一个很好的博客 post 事实上..
编辑: 但我仍然不明白为什么我首先遇到这种类型推断问题,我已经更新了问题。