标签:编程 scala functional programmi
Monoid是数学范畴理论(category theory)中的一个特殊范畴(category)。不过我并没有打算花时间从范畴理论的角度去介绍Monoid,而是希望从一个程序员的角度去分析Monoid以及它在泛函编程里的作用。从这个思路出发我们很自然得出Monoid就是一种数据类型,或者是一种在泛函编程过程中经常会遇到的数据类型:当我们针对List或者loop进行一个数值的积累操作时我们就会使用到Monoid。实际上Monoid就是List[A] => A的抽象模型。好了,我们就不要越描越黑了吧,还是看看Monoid的定义吧:
Monoid由以下条件组成:
1、一个抽象类型A
2、一个二元结合性函数(binary associative function),对传入的两个A类参数进行操作后产生一个A类型结果
3、一个恒等值(identity)
由于Monoid是一个数学类型,它的二元操作函数必须遵循一些定律:
1、结合性(associativity):op(a,op(b,c)) = op(op(a,b),c):这个定律是函数组合(function composition)不可缺的条件
2、二元函数参数中如果有一个是恒等值时操作结果为另一个参数:op(identity,v) = v
我们可以用编程语言来描述Monoid:
trait Monoid[A] { //被封装的类型A def op(a1: A, a2: A): A //二元函数 val zero: A //恒等值identity }
既然Monoid trait是个抽象类型,那么我们可以试着创建几个基础类型的Monoid实例:
val stringConcatMonoid = new Monoid[String] { def op(s1: String, s2: String) = s1 + s2 val zero = "" // op(zero,s2) = "" + s2 = s2 恒等值定律 } //> stringConcatMonoid : ch10.ex1.Monoid[String] = ch10.ex1$$anonfun$main$1$$an //| on$1@3581c5f3
val intAdditionMonoid = new Monoid[Int] { def op(i1: Int, i2: Int) = i1 + i2 val zero = 0 } //> intAdditionMonoid : ch10.ex1.Monoid[Int] = ch10.ex1$$anonfun$main$1$$anon$4 //| @340f438e val intMultiplicationMonoid = new Monoid[Int] { def op(i1: Int, i2: Int) = i1 * i2 val zero = 1 } //> intMultiplicationMonoid : ch10.ex1.Monoid[Int] = ch10.ex1$$anonfun$main$1$$ //| anon$5@30c7da1e
def reduce[A](as: List[A])(m: Monoid[A]): A = { as match { case Nil => m.zero case h::t => m.op(h, reduce(t)(m)) } } //> reduce: [A](as: List[A])(m: ch10.ex1.Monoid[A])A
reduce(List(1,2,3))(intAdditionMonoid) //> res3: Int = 6 reduce(List("this is ","the string", " monoid"))(stringConcatMonoid) //> res4: String = this is the string monoid
reduce[A](as: List[A])(zero: A)(op: (A,A) => A) : A
def foldRight[A,B](as: List[A])(z: B)(f: (A,B) => B): B 如果类型B=类型A def foldRight[A](as: List[A])(z: A)(f: (A,A) => A): A
List(1,2,3).foldRight(intAdditionMonoid.zero)(intAdditionMonoid.op) //> res3: Int = 6 List("this is ","the string", " monoid").foldLeft(stringConcatMonoid.zero)(stringConcatMonoid.op) //> res4: String = this is the string monoid
下面我们再试着增加几个Monoid实例:
def optionMonoid[A] = new Monoid[Option[A]] { def op(o1: Option[A], o2: Option[A]): Option[A] = o1 orElse o2 val zero = None // op(zero, o1)= None orElse o2 = o2 } //> optionMonoid: [A]=> ch10.ex1.Monoid[Option[A]]{val zero: None.type} def listConcatMonoid[A] = new Monoid[List[A]] { def op(l1: List[A], l2: List[A]) = l1 ++ l2 val zero = Nil } //> listConcatMonoid: [A]=> ch10.ex1.Monoid[List[A]]{val zero: scala.collection. //| immutable.Nil.type}
val booleanOrMonoid = new Monoid[Boolean] { def op(b1: Boolean, b2: Boolean) = b1 || b2 val zero = false } //> booleanOrMonoid : ch10.ex1.Monoid[Boolean] = ch10.ex1$$anonfun$main$1$$anon //| $6@5b464ce8 val booleanAndMonoid = new Monoid[Boolean] { def op(b1: Boolean, b2: Boolean) = b1 && b2 val zero = true } //> booleanAndMonoid : ch10.ex1.Monoid[Boolean] = ch10.ex1$$anonfun$main$1$$an //| on$7@57829d67 def endoComposeMonoid[A] = new Monoid[A => A] { def op(f: A => A, g: A => A) = f compose g val zero = (a: A) => a // op(zero, g: A => A) = zero compose g = g } //> endoComposeMonoid: [A]=> ch10.ex1.Monoid[A => A] def endoAndThenMonoid[A] = new Monoid[A => A] { def op(f: A => A, g: A => A) = f andThen g val zero = (a: A) => a // op(zero, g: A => A) = zero andThen g = g } //> endoAndThenMonoid: [A]=> ch10.ex1.Monoid[A => A] //计算m的镜像Monoid def dual[A](m: Monoid[A]) = new Monoid[A] { def op(x: A, y: A) = m.op(y,x) //镜像op即时二元参数位置互换 val zero = m.zero } //> dual: [A](m: ch10.ex1.Monoid[A])ch10.ex1.Monoid[A] def firstOfDualOptionMonoid[A] = optionMonoid[A] //> firstOfDualOptionMonoid: [A]=> ch10.ex1.Monoid[Option[A]]{val zero: None.ty //| pe} def secondOfDualOptionMonoid[A] = dual(firstOfDualOptionMonoid[A]) //> secondOfDualOptionMonoid: [A]=> ch10.ex1.Monoid[Option[A]]
还是回到对List[A]的累加操作。下面这个函数用Monoid对List[A]元素A进行累加操作:
def concatenate[A](l: List[A], m: Monoid[A]): A = { l.foldRight(m.zero){(a,b) => m.op(a,b)} } //> concatenate: [A](l: List[A], m: ch10.ex1.Monoid[A])A
concatenate[Int](List(1,2,3),intAdditionMonoid) //> res0: Int = 6
那么如果没有List[A]元素A类型Monoid实例怎么办?我们可以加一个函数:
def foldMap[A,B](as: List[A])(m: Monoid[B])(f: A => B): B
def foldMap[A,B](as: List[A])(m: Monoid[B])(f: A => B): B = { as.foldRight(m.zero)((a,b) => m.op(f(a),b)) }
def foldRight[A,B](la: List[A])(z: B)(f: (A,B) => B): B
def foldLeft[A,B](la: List[A])(z: B)(f: (A,B) => B): B
def foldMap[A,B](as: List[A])(m: Monoid[B])(f: A => B): B
def foldRight[A,B](as: List[A])(z: B)(f: (A,B) => B): B = { foldMap(as)(endoComposeMonoid[B])(a => b => f(a,b))(z) }
foldLeft是从左边开始折叠,只需要采用endoComposeMonoid的镜像Monoid把op参数位置调换就行了:
def foldLeft[A,B](as: List[A])(z: B)(f: (A,B) => B): B = { foldMap(as)(dual(endoComposeMonoid[B]))(a => b => f(a,b))(z) }
标签:编程 scala functional programmi
原文地址:http://blog.csdn.net/tiger_xc/article/details/45153073