知乎里有关于 什么是Monad 的问题讨论,而在维基百科中也有关于 Monad的释义 )。作为初次接触到Monads概念,难免会有些晕头转向,也难免会有些畏惧(因为Monads和数学中的范畴论有密切关系),但是Monads又是如此的重要,因为它在函数式编程中实在是应用太广泛了,并且在Scala的标准库中又常常遇到,使得我们不得不好好研究一番。
Monoids是一种元素的集合,它需要满足结合律和幺元(Identity,也称为单位元,这种元和其他元素结合时,不会改变那么元素)这些约束条件。比如:
:::
连接起来,其中Nil或空列表[]是Identity Monoids在平常的编程之中无处不在,当用到一个列表,连接字符串,通过一个循环得到一个累加结果,都在使用到Monoids。
Monoid可以用下面的代码描述:
trait Monoid[T] { defop(m1: T, m2: T):T val identity: T }
这个特质可以被混入类型(classes)、对象(objects)或者其他特质中。请看下面的举例:
caseclassStringMonoidextendsMonoid[String]{ defop(s1: String, s2: String) = s1 + s2 validentity ="" } valstringMonoid = StringMonoid() println(stringMonoid.op(stringMonoid.identity, "John")) println(stringMonoid.op("John","Hunt")) // Output is // John // JohnHunt objectIntMonoidextendsMonoid[Int]{ defop(x: Int, y: Int) = x + y validentity =0 } println(IntMonoid.op(IntMonoid.identity, 1)) println(IntMonoid.op(1,2)) println(IntMonoid.op(2,1)) // Output is // 1 // 3 // 3
如果有一个Monoid结构和一组数据。可以通过对每个元素进行Monoid的op操作来将集合缩减为一个值,比如将一个整数列表通过元素累加的方式得到所有整数的和。
Monoid和List有着密切的联系。在List的foldLeft操作中,用一个初始元素从列表的左边元素开始操作,一直到对所有元素都操作完。如 List("A", "B", "C").foldLeft("")(_ + _)
这个对字符串列表实现累加功能,foldLeft传入的两个参数分别是空字符串和二元操作运算,这正好符合Monoid的定义,可以轻松利用StringMonoid代替, List("A", "B", "C").foldLeft(StringMonoid.identity)(StringMonoid.op)
。
Monoid的结合性意味着我们在对类似List的数据结构进行折叠的时候有很大的灵活性。我们已经知道可以使用foldLeft和foldRight对一个列表进行顺序的规则(reduce)操作。但是我们同样可以将数据分成多份,并行的进行折叠,然后利用monoid将各个部分合并起来。
左折叠操作是op(op(op(a, b), c), d)
op(a, op(b, op(c, d)))
并行算法为 op(op(a, b), op(c, d))
,其中op(a, b)和op(c, d)是同时运算的。
如果我们对一个超大文件进行文字数统计或者寻找最大值什么的,我们可以把这个大文件分成若干小文件然后同时计算后再合计将节省很多计算时间。
优点:
缺点:
从Monoid到Monad,这些概念都是从范畴论中衍生出来的。理解范畴论的一个好方法是把它理解为应用到函数式编程领域的设计模式。范畴论定义了一些非常底层的概念抽象,这些概念可以直接用Scala这样的支持函数式编程的语言表达。在设计软件的时候,如果一个特定实体符合其中一个概念,那么立刻就有一整组操作可用,而且包含推理其用法的方法。
范畴是由元素对象和态射箭头组成的,这个箭头开始端是一个元素对象,目的地也是一个元素对象。这里态射箭头有两种,不同元素对象比如a和b之间的态射箭头称为组合箭头,而指向自己的箭头称为元箭头,或者单元,幺元。
范畴的元素对象和箭头态射的规则如下:
我们将一个范畴有元素对象和态射箭头,态射箭头有组合和幺元两种,且满足结合律,这种范畴称为Monoid。
对于某非空集合S,若存在S上的二元运算”*“使得对于任意的a,b∈S,有a b∈S(运算封闭),则称{S,/ }为广群。 广群只是定义一个集合,集合中有元素和操作,操作结果也属于这个集合,这样泛泛的集合称为广群。 如果广群再加上结合律约束,就会得到半群,因此半群是广群的子集,要求更苛刻些,而半群如果再加上幺元(identity element)就是幺半群,也就是结合律+幺元=幺半群,所以,Monid对应的中文是幺半群。
Functional Programming in Scala
Scala Design Patterns: Patterns for Practical Reuse and Design
什么是Monoid?
我所理解的monad(2):fold与monoid转载请注明作者Jason Ding及其出处
Github博客主页(http://jasonding1354.github.io/)
GitCafe博客主页(http://jasonding1354.gitcafe.io/)
CSDN博客(http://blog.csdn.net/jasonding1354)
简书主页(http://www.jianshu.com/users/2bd9b48f6ea8/latest_articles)
Google搜索jasonding1354进入我的博客主页