trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
def mapMergeMonoid[K, V](V: Monoid[V]): Monoid[Map[K, V]] = new Monoid[Map[K, V]] {
override def op(a1: Map[K, V], a2: Map[K, V]): Map[K, V] =
(a1.keySet ++ a2.keySet).foldLeft(zero) {
(acc, k) => acc.updated(k, V.op(a1.getOrElse(k, V.zero), a2.getOrElse(k, V.zero)))
}
override def zero: Map[K, V] = Map[K, V]()
}
据我了解,我可以将 2 张地图与这个 Monoid 连接起来。但我不明白,如何使用它。 我必须放入(V: Monoid[V])
论点才能使用op
方法,并放在那里 2 张地图。
Monoid
是一个类型类。因此,建议学习如何在Scala中对它们进行建模,这是使用implicits
.
具体来说,Monoid[Map[K, V]]
的情况是称为类型类推导的情况,因为我们首先需要一个证明V
有一个Monoid
,以证明Map[K, V]
也有一个,对于所有Ks
。
这是定义此类类型类的规范方法,以及它的实例以及它们的操作/语法。
trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
object Monoid {
implicit final val IntMonoid: Monoid[Int] =
new Monoid[Int] {
override final def op(i1: Int, i2: Int): Int =
i1 + i2
override final val zero: Int = 0
}
implicit def mapMonoid[K, V](implicit vm: Monoid[V]): Monoid[Map[K, V]] =
new Monoid[Map[K, V]] {
override final def op(m1: Map[K, V], m2: Map[K, V]): Map[K, V] =
(m1.keySet | m2.keySet).foldLeft(this.zero) {
case (acc, key) =>
acc + (key -> vm.op(
m1.getOrElse(key, default = vm.zero),
m2.getOrElse(key, default = vm.zero)
))
}
override final val zero: Map[K, V] = Map.empty
}
}
object syntax {
object monoid {
implicit class MonoidOps[A] (private val a1: A) {
def |+| (a2: A)(implicit M: Monoid[A]): A =
M.op(a1, a2)
}
}
}
然后你可以像这样使用:
import syntax.monoid._ // Provides the |+| operator.
Map('a' -> 1, 'b' -> 2) |+| Map('b' -> 3, 'c' -> 5)
// res: scala.collection.immutable.Map[Char,Int] = Map(a -> 1, b -> 5, c -> 5)
最后,值得一提的是,虽然我相信第一次手工做这些事情对于真正了解它们在引擎盖下的工作原理是很棒的。鼓励使用稳定且生产就绪的库,这些库提供这些抽象,例如Cats。
斯卡拉菲德尔
假设我们要合并两个类型为Map[Int, String]
的映射
val a1: Map[Int, String] = Map(1 -> "Picard")
val a2: Map[Int, String] = Map(1 -> "Worf", 2 -> "Data")
然后V
变得String
这意味着我们需要提供Monoid[String]
实例,以便指定V
s将如何组合
val stringMonoid: Monoid[String] = new Monoid[String] {
override def op(a1: String, a2: String) = a1 + a2
override def zero = ""
}
把它放在一起,我们有
mapMergeMonoid(stringMonoid).op(a1, a2)
哪些输出
res0: Map[Int,String] = Map(1 -> PicardWorf, 2 -> Data)
从概念上讲,幺半群提供了一种组合值的方法,因此在定义如何组合Map[K, V]
类型的映射时,我们还需要指定映射的值V
如何组合自己。因此Monoid[V]
是Monoid[Map[K, V]]
定义的必要构成要素:
如果
Map[A, B]
是Monoid
,B
则Monoid