接上文,因为半群里的“结合律”特性,使得我们可以对一些任务拆分采用并行处理,只要这些任务的结果类型符合“结合律”(即没有先后依赖)。让我们看一个单词统计的例子,阿里中间件团队前几个月有过一次编程比赛就是统计单词频度,参考:Coding4Fun第三期活动总结
编程比赛主要是考察技巧,现在我们看看现实世界中用scala怎么解决这个问题,如果数据是巨大的,无疑要采用map-reduce的思路,如果我们不依赖hadoop之类的系统来解决,最简单的方式就是actor处理,最后再汇总结果。这篇blog不讨论数据的统计处理,看看最后如何把这些结果合并
type Result = Map[String,Int]
object combiner extends SemiGroup[Result] {
def append(x: Result, y: Result): Result = {
val x0 = x.withDefaultValue(0)
val y0 = y.withDefaultValue(0)
val keys = x.keys.toSet.union(y.keys.toSet)
keys.map{ k => (k -> (x0(k) + y0(k))) }.toMap
} //不考虑效率
}
现在假设不同的actor分别返回r1,r2
val r1 = Map("hello" -> 1, "world" -> 2)
val r2 = Map("hello" -> 2, "ok" -> 5)
val counts = combiner.append(r1, r2)
如果有其他actor再返回结果的话,只要继续合并下去:
combiner.append(r3, counts)
twitter开源的algebird,就是基于semigroup/monoid的。不过我们不必沿着这条路深入下去,monad并不是为了并发而发明的,只是它正好是一个半群(幺半群),半群的结合律符合并行运算(由半群演化出来的迹幺半群和历史幺半群是进程演算和并行计算的基础)。这是另一个方向,不继续涉及下去,我们后续回到monad的其他特征上。
感谢,醍醐灌顶
确实醍醐灌顶,我第一次理解幺半群,是根据mapreduce的,,虽然现在看,开启combian环节后更像交换幺半群
巧遇。。