zoukankan      html  css  js  c++  java
  • Scalaz(8)- typeclass:Monoid and Foldable

      Monoid是种最简单的typeclass类型。我们先看看scalaz的Monoid typeclass定义:scalaz/Monoid.scala

    1 trait Monoid[F] extends Semigroup[F] { self =>
    2   ////
    3   /** The identity element for `append`. */
    4   def zero: F
    5 ...

    Monoid trait又继承了Semigroup:scalaz/Semigroup.scala

     1 trait Semigroup[F]  { self =>
     2   ////
     3   /**
     4    * The binary operation to combine `f1` and `f2`.
     5    *
     6    * Implementations should not evaluate the by-name parameter `f2` if result
     7    * can be determined by `f1`.
     8    */
     9   def append(f1: F, f2: => F): F
    10 ...

    所以获取一个类型的Monoid实例需要实现zero和append这两个抽象函数。实际上Monoid typeclass也就是支持了append(|+|)这么一个简单的操作。scalaz为一些标准类型定义了Monoid实例:

    1 0 |+| 30                                         //> res0: Int = 50
    2 20.some |+| 30.some                               //> res1: Option[Int] = Some(50)
    3 List(1,2,3) |+| List(4,5,6)                       //> res2: List[Int] = List(1, 2, 3, 4, 5, 6)
    4 Tags.Multiplication(3) |+| Monoid[Int @@ Tags.Multiplication].zero
    5                                                   //> res3: scalaz.@@[Int,scalaz.Tags.Multiplication] = 3
    6 Tags.Conjunction(true) |+| Tags.Conjunction(false)//> res4: scalaz.@@[Boolean,scalaz.Tags.Conjunction] = false
    7 Tags.Disjunction(true) |+| Tags.Disjunction(false)//> res5: scalaz.@@[Boolean,scalaz.Tags.Disjunction] = true
    8 Monoid[Boolean @@ Tags.Conjunction].zero          //> res6: scalaz.@@[Boolean,scalaz.Tags.Conjunction] = true
    9 Monoid[Boolean @@ Tags.Disjunction].zero          //> res7: scalaz.@@[Boolean,scalaz.Tags.Disjunction] = false

    就这么来看好像没什么值得提的。不过Ordering的Monoid倒是值得研究一下。我们先看看Ordering trait:scalaz/Ordering.scala

     1  implicit val orderingInstance: Enum[Ordering] with Show[Ordering] with Monoid[Ordering] = new Enum[Ordering] with Show[Ordering] with Monoid[Ordering] {
     2     def order(a1: Ordering, a2: Ordering): Ordering = (a1, a2) match {
     3       case (LT, LT)      => EQ
     4       case (LT, EQ | GT) => LT
     5       case (EQ, LT)      => GT
     6       case (EQ, EQ)      => EQ
     7       case (EQ, GT)      => LT
     8       case (GT, LT | EQ) => GT
     9       case (GT, GT)      => EQ
    10     }
    11 
    12     override def shows(f: Ordering) = f.name
    13 
    14     def append(f1: Ordering, f2: => Ordering): Ordering = f1 match {
    15       case Ordering.EQ => f2
    16       case o           => o
    17     }
    18 ...

    这里定义了Ordering的Monoid实例。它的append函数意思是:两个Ordering类型值f1,f2的append操作结果:假如f1是EQ就是f2,否则是f1:

     1 (Ordering.EQ: Ordering) |+| (Ordering.GT: Ordering)
     2                                                   //> res8: scalaz.Ordering = GT
     3 (Ordering.EQ: Ordering) |+| (Ordering.LT: Ordering)
     4                                                   //> res9: scalaz.Ordering = LT
     5 (Ordering.GT: Ordering) |+| (Ordering.EQ: Ordering)
     6                                                   //> res10: scalaz.Ordering = GT
     7 (Ordering.LT: Ordering) |+| (Ordering.EQ: Ordering)
     8                                                   //> res11: scalaz.Ordering = LT
     9 (Ordering.LT: Ordering) |+| (Ordering.GT: Ordering)
    10                                                   //> res12: scalaz.Ordering = LT
    11 (Ordering.GT: Ordering) |+| (Ordering.LT: Ordering)
    12                                                   //> res13: scalaz.Ordering = GT

    如果我用以上的特性来比较两个String的长度:如果长度相等则再比较两个String的字符顺序。这个要求刚好符合了Ordering Monoid实例的append操作:

    1 3 ?|? 4                                           //> res14: scalaz.Ordering = LT
    2 "abc" ?|? "bac"                                   //> res15: scalaz.Ordering = LT
    3 def strlenCompare(lhs: String, rhs: String): Ordering =
    4  (lhs.length ?|? rhs.length) |+| (lhs ?|? rhs)    //> strlenCompare: (lhs: String, rhs: String)scalaz.Ordering
    5 
    6 strlenCompare("abc","aabc")                       //> res16: scalaz.Ordering = LT
    7 strlenCompare("abd","abc")                        //> res17: scalaz.Ordering = GT

    这个示范倒是挺新鲜的。

    好了,单看Monoid操作会觉着没什么特别,好像不值得研究。实际上Monoid的主要用途是在配合可折叠数据结构(Foldable)对结构内部元素进行操作时使用的。我们再看看这个Foldable typeclass:scalaz/Foldable.scala

     1 trait Foldable[F[_]]  { self =>
     2   ////
     3   import collection.generic.CanBuildFrom
     4   import collection.immutable.IndexedSeq
     5 
     6   /** Map each element of the structure to a [[scalaz.Monoid]], and combine the results. */
     7   def foldMap[A,B](fa: F[A])(f: A => B)(implicit F: Monoid[B]): B
     8   /** As `foldMap` but returning `None` if the foldable is empty and `Some` otherwise */
     9   def foldMap1Opt[A,B](fa: F[A])(f: A => B)(implicit F: Semigroup[B]): Option[B] = {
    10     import std.option._
    11     foldMap(fa)(x => some(f(x)))
    12   }
    13 
    14   /**Right-associative fold of a structure. */
    15   def foldRight[A, B](fa: F[A], z: => B)(f: (A, => B) => B): B
    16 ...

    Foldable typeclass提供了许多注入方法支持折叠操作: scalaz/syntax/FoldableSyntax.scala

     1 final class FoldableOps[F[_],A] private[syntax](val self: F[A])(implicit val F: Foldable[F]) extends Ops[F[A]] {
     2   ////
     3   import collection.generic.CanBuildFrom
     4   import Leibniz.===
     5   import Liskov.<~<
     6 
     7   final def foldMap[B: Monoid](f: A => B = (a: A) => a): B = F.foldMap(self)(f)
     8   final def foldMap1Opt[B: Semigroup](f: A => B = (a: A) => a): Option[B] = F.foldMap1Opt(self)(f)
     9   final def foldRight[B](z: => B)(f: (A, => B) => B): B = F.foldRight(self, z)(f)
    10   final def foldMapRight1Opt[B](z: A => B)(f: (A, => B) => B): Option[B] = F.foldMapRight1Opt(self)(z)(f)
    11   final def foldRight1Opt(f: (A, => A) => A): Option[A] = F.foldRight1Opt(self)(f)
    12   final def foldLeft[B](z: B)(f: (B, A) => B): B = F.foldLeft(self, z)(f)
    13   final def foldMapLeft1Opt[B](z: A => B)(f: (B, A) => B): Option[B] = F.foldMapLeft1Opt(self)(z)(f)
    14   final def foldLeft1Opt(f: (A, A) => A): Option[A] = F.foldLeft1Opt(self)(f)
    15   final def foldRightM[G[_], B](z: => B)(f: (A, => B) => G[B])(implicit M: Monad[G]): G[B] = F.foldRightM(self, z)(f)
    16   final def foldLeftM[G[_], B](z: B)(f: (B, A) => G[B])(implicit M: Monad[G]): G[B] = F.foldLeftM(self, z)(f)
    17   final def foldMapM[G[_] : Monad, B : Monoid](f: A => G[B]): G[B] = F.foldMapM(self)(f)
    18   final def fold(implicit A: Monoid[A]): A = F.fold(self)(A)
    19   final def foldr[B](z: => B)(f: A => (=> B) => B): B = F.foldr(self, z)(f)
    20   final def foldr1Opt(f: A => (=> A) => A): Option[A] = F.foldr1Opt(self)(f)
    21   final def foldl[B](z: B)(f: B => A => B): B = F.foldl(self, z)(f)
    22   final def foldl1Opt(f: A => A => A): Option[A] = F.foldl1Opt(self)(f)
    23   final def foldrM[G[_], B](z: => B)(f: A => ( => B) => G[B])(implicit M: Monad[G]): G[B] = F.foldrM(self, z)(f)
    24   final def foldlM[G[_], B](z: B)(f: B => A => G[B])(implicit M: Monad[G]): G[B] = F.foldlM(self, z)(f)
    25   final def length: Int = F.length(self)
    26   final def index(n: Int): Option[A] = F.index(self, n)
    27   final def indexOr(default: => A, n: Int): A = F.indexOr(self, default, n)
    28   final def sumr(implicit A: Monoid[A]): A = F.foldRight(self, A.zero)(A.append)
    29   final def suml(implicit A: Monoid[A]): A = F.foldLeft(self, A.zero)(A.append(_, _))
    30   final def toList: List[A] = F.toList(self)
    31   final def toVector: Vector[A] = F.toVector(self)
    32   final def toSet: Set[A] = F.toSet(self)
    33   final def toStream: Stream[A] = F.toStream(self)
    34   final def toIList: IList[A] = F.toIList(self)
    35   final def toEphemeralStream: EphemeralStream[A] = F.toEphemeralStream(self)
    36   final def to[G[_]](implicit c: CanBuildFrom[Nothing, A, G[A]]) = F.to[A, G](self)
    37   final def all(p: A => Boolean): Boolean = F.all(self)(p)
    38   final def ∀(p: A => Boolean): Boolean = F.all(self)(p)
    39   final def allM[G[_]: Monad](p: A => G[Boolean]): G[Boolean] = F.allM(self)(p)
    40   final def anyM[G[_]: Monad](p: A => G[Boolean]): G[Boolean] = F.anyM(self)(p)
    41   final def any(p: A => Boolean): Boolean = F.any(self)(p)
    42   final def ∃(p: A => Boolean): Boolean = F.any(self)(p)
    43   final def count: Int = F.count(self)
    44   final def maximum(implicit A: Order[A]): Option[A] = F.maximum(self)
    45   final def maximumOf[B: Order](f: A => B): Option[B] = F.maximumOf(self)(f)
    46   final def maximumBy[B: Order](f: A => B): Option[A] = F.maximumBy(self)(f)
    47   final def minimum(implicit A: Order[A]): Option[A] = F.minimum(self)
    48   final def minimumOf[B: Order](f: A => B): Option[B] = F.minimumOf(self)(f)
    49   final def minimumBy[B: Order](f: A => B): Option[A] = F.minimumBy(self)(f)
    50   final def longDigits(implicit d: A <:< Digit): Long = F.longDigits(self)
    51   final def empty: Boolean = F.empty(self)
    52   final def element(a: A)(implicit A: Equal[A]): Boolean = F.element(self, a)
    53   final def splitWith(p: A => Boolean): List[NonEmptyList[A]] = F.splitWith(self)(p)
    54   final def selectSplit(p: A => Boolean): List[NonEmptyList[A]] = F.selectSplit(self)(p)
    55   final def collapse[X[_]](implicit A: ApplicativePlus[X]): X[A] = F.collapse(self)
    56   final def concatenate(implicit A: Monoid[A]): A = F.fold(self)
    57   final def intercalate(a: A)(implicit A: Monoid[A]): A = F.intercalate(self, a)
    58   final def traverse_[M[_]:Applicative](f: A => M[Unit]): M[Unit] = F.traverse_(self)(f)
    59   final def traverseU_[GB](f: A => GB)(implicit G: Unapply[Applicative, GB]): G.M[Unit] =
    60     F.traverseU_[A, GB](self)(f)(G)
    61   final def traverseS_[S, B](f: A => State[S, B]): State[S, Unit] = F.traverseS_(self)(f)
    62   final def sequence_[G[_], B](implicit ev: A === G[B], G: Applicative[G]): G[Unit] = F.sequence_(ev.subst[F](self))(G)
    63   final def sequenceS_[S, B](implicit ev: A === State[S,B]): State[S,Unit] = F.sequenceS_(ev.subst[F](self))
    64   def sequenceF_[M[_],B](implicit ev: F[A] <~< F[Free[M,B]]): Free[M, Unit] = F.sequenceF_(ev(self))
    65   final def msuml[G[_], B](implicit ev: A === G[B], G: PlusEmpty[G]): G[B] = F.foldLeft(ev.subst[F](self), G.empty[B])(G.plus[B](_, _))
    66   ////
    67 }

    这简直就是一个完整的函数库嘛。scalaz为大多数标准库中的集合类型提供了Foldable实例,也就是说大多数scala集合类型都支持这么一堆折叠操作函数。我还看不到任何需要去自定义集合类型,标准库的集合类型加上Foldable typeclass应该足够用了。

    在Foldable typeclass中比较重要的函数就是foldMap了:

    1 trait Foldable[F[_]]  { self =>
    2   ////
    3   import collection.generic.CanBuildFrom
    4   import collection.immutable.IndexedSeq
    5 
    6   /** Map each element of the structure to a [[scalaz.Monoid]], and combine the results. */
    7   def foldMap[A,B](fa: F[A])(f: A => B)(implicit F: Monoid[B]): B

    首先,foldMap需要Monoid[B]实例来实现。用List来举例:List trait 继承了Traverse:scalaz/std/List.scala

    1 trait ListInstances extends ListInstances0 {
    2   implicit val listInstance = new Traverse[List] with MonadPlus[List] with Zip[List] with Unzip[List] with Align[List] with IsEmpty[List] with Cobind[List] {
    3 ...

    在Traverse typeclass里定义了Foldable实例:scalaz/Traverse.scala

     1  def foldLShape[A,B](fa: F[A], z: B)(f: (B,A) => B): (B, F[Unit]) =
     2     runTraverseS(fa, z)(a => State.modify(f(_, a)))
     3 
     4   override def foldLeft[A,B](fa: F[A], z: B)(f: (B,A) => B): B = foldLShape(fa, z)(f)._1
     5 
     6   def foldMap[A,B](fa: F[A])(f: A => B)(implicit F: Monoid[B]): B = foldLShape(fa, F.zero)((b, a) => F.append(b, f(a)))._1
     7 
     8   override def foldRight[A, B](fa: F[A], z: => B)(f: (A, => B) => B) =
     9     foldMap(fa)((a: A) => (Endo.endo(f(a, _: B)))) apply z
    10 ...

    这个foldMap就是一个游览可折叠结构的函数。在游览过程中用Monoid append对结构中元素进行操作。值得注意的是这个f: A => B参数:这个函数是用来在append操作之前先对内部元素进行一次转变(transform):

    1 List(1,2,3) foldMap {x => x}                      //> res18: Int = 6
    2 List(1,2,3) foldMap {x => (x + 3).toString}       //> res19: String = 456 变成String操作

    我们试着用一些实际的例子来示范Monoid的用法。上面提到Monoid在可折叠数据结构里的元素连续处理有着很好的应用,我们先试一个例子:确定一个可折叠数据结构F[A]中的元素A是否排序的:

    def ordered(xs: List[Int]): Boolean  //判断xs是否按序排列

    由于我们必须游览List xs,所以用Monoid对元素Int进行判断操作是可行的方法。我们先设计一个对比数据结构:

    Option[(min: Int, max: Int. ordered: Boolean)], 它记录了当前元素的状态,包括最小,最大,是否排序的:

     1 /判断xs是否是排序的
     2 def ordered(xs: List[Int]): Boolean = {
     3     val monoid = new Monoid[Option[(Int,Int,Boolean)]] {  //对类型Option[(Int,Int,Boolean)]定义一个Monoid实例
     4         def zero = None
     5         def append(a1: Option[(Int,Int,Boolean)], a2: => Option[(Int,Int,Boolean)]) =  //对连续两个元素进行对比操作
     6           (a1,a2) match {
     7             case (x,None) => x
     8             case (None,x) => x    //保留不为None的状态
     9             case (Some((min1,max1,ord1)),Some((min2,max2,ord2))) =>  //如果max1 <= min2状态即为true
    10                  Some((min1 min min2, max1 max max2, ord1 && ord2 && (max1 <= min2)))  //更新min,max和ord
    11           }
    12     }  //我们需要把元素转换成Option((Int,Int,Boolean))
    13     (xs.foldMap(i => Option((i, i, true)))(monoid)).map(_._3) getOrElse(true)
    14 }                                                 //> ordered: (xs: List[Int])Boolean
    15 
    16 ordered(List(1,2,12,34))                          //> res21: Boolean = true
    17 ordered(List(1,2,34,23))                          //> res22: Boolean = false

    注意这个i => Option((i,i,true)) 转换(transform)。

    由于Monoid是种极简单的类型,所以很容易对Monoid进行组合。Monoid组合产生的结果还是Monoid,并且用起来可以更方便:

    1 def productMonoid[A,B](ma: Monoid[A], mb: Monoid[B]): Monoid[(A,B)] = new Monoid[(A,B)] {
    2     def zero = (ma.zero, mb.zero)
    3     def append(x: (A,B), y: => (A,B)): (A,B) = (ma.append(x._1, y._1), mb.append(x._2, y._2))
    4 }                                                 //> productMonoid: [A, B](ma: scalaz.Monoid[A], mb: scalaz.Monoid[B])scalaz.Mon
    5                                                   //| oid[(A, B)]
    6 val pm = productMonoid(Monoid[Int],Monoid[List[Int]])
    7                                                   //> pm  : scalaz.Monoid[(Int, List[Int])] = Exercises.monoid$$anonfun$main$1$$a
    8                                                   //| non$3@72d1ad2e

    以上的pm就是两个Monoid的组合,结果是一个tuple2Monoid。我们可以使用这个tuple2Monoid对可折叠数据结构中元素进行并行操作。比如我们可以在游览一个List[Int]时同时统计长度(list length)及乘积(product):

     1 val intMultMonoid = new Monoid[Int] {
     2     def zero = 1
     3     def append(a1: Int, a2: => Int): Int = a1 * a2
     4 }                                                 //> intMultMonoid  : scalaz.Monoid[Int] = Exercises.monoid$$anonfun$main$1$$ano
     5                                                   //| n$1@6c64cb25
     6 def productMonoid[A,B](ma: Monoid[A], mb: Monoid[B]): Monoid[(A,B)] = new Monoid[(A,B)] {
     7     def zero = (ma.zero, mb.zero)
     8     def append(x: (A,B), y: => (A,B)): (A,B) = (ma.append(x._1, y._1), mb.append(x._2, y._2))
     9 }                                                 //> productMonoid: [A, B](ma: scalaz.Monoid[A], mb: scalaz.Monoid[B])scalaz.Mon
    10                                                   //| oid[(A, B)]
    11 val pm = productMonoid(Monoid[Int @@ Tags.Multiplication],Monoid[Int])
    12                                                   //> pm  : scalaz.Monoid[(scalaz.@@[Int,scalaz.Tags.Multiplication], Int)] = Exe
    13                                                   //| rcises.monoid$$anonfun$main$1$$anon$3@72d1ad2e
    14 List(1,2,3,4,6).foldMap(i => (i, 1))(productMonoid(intMultMonoid,Monoid[Int]))
    15                                                   //> res23: (Int, Int) = (144,5)

    我们再来一个合并多层map的Monoid:

     1 def mapMergeMonoid[K,V](V: Monoid[V]): Monoid[Map[K, V]] =
     2   new Monoid[Map[K, V]] {
     3     def zero = Map[K,V]()
     4     def append(a: Map[K, V], b: => Map[K, V]) =
     5       (a.keySet ++ b.keySet).foldLeft(zero) { (acc,k) =>
     6         acc.updated(k, V.append(a.getOrElse(k, V.zero),
     7                             b.getOrElse(k, V.zero)))
     8       }
     9   }                                               //> mapMergeMonoid: [K, V](V: scalaz.Monoid[V])scalaz.Monoid[Map[K,V]]
    10     
    11  val M: Monoid[Map[String, Map[String, Int]]] = mapMergeMonoid(mapMergeMonoid(Monoid[Int]))
    12                                                   //> M  : scalaz.Monoid[Map[String,Map[String,Int]]] = Exercises.monoid$$anonfun
    13                                                   //| $main$1$$anon$4@79e2c065
    14  val m1 = Map("o1" -> Map("i1" -> 1, "i2" -> 2))  //> m1  : scala.collection.immutable.Map[String,scala.collection.immutable.Map[
    15                                                   //| String,Int]] = Map(o1 -> Map(i1 -> 1, i2 -> 2))
    16  val m2 = Map("o1" -> Map("i2" -> 3))             //> m2  : scala.collection.immutable.Map[String,scala.collection.immutable.Map[
    17                                                   //| String,Int]] = Map(o1 -> Map(i2 -> 3))
    18  val m3 = M.append(m1, m2)                        //> m3  : Map[String,Map[String,Int]] = Map(o1 -> Map(i1 -> 1, i2 -> 5))

    我们可以用这个组合成的M的append操作进行map的深度合并。m1,m2合并后:Map(o1->Map("i1"->1,"i2" -> 5))。

    我们还可以用这个Monoid来统计一段字串内字符发生的频率:

    1 def frequencyMap[A](as: List[A]): Map[A, Int] =
    2     as.foldMap((a: A) => Map(a -> 1))(mapMergeMonoid[A, Int](Monoid[Int]))
    3                                                   //> frequencyMap: [A](as: List[A])Map[A,Int]
    4 frequencyMap("the brown quik fox is running quikly".toList)
    5   //> res24: Map[Char,Int] = Map(e -> 1, s -> 1, x -> 1, n -> 4, y -> 1, t -> 1, 
    6   //| u -> 3, f -> 1, i -> 4,   -> 6, q -> 2, b -> 1, g -> 1, l -> 1, h -> 1, r -
    7   //| > 2, w -> 1, k -> 2, o -> 2)

    我们现在可以体会到Monoid必须在可折叠数据结构(Foldable)内才能正真发挥作用。

  • 相关阅读:
    linux sysfs (2)
    微软——助您启动云的力量网络虚拟盛会
    Windows Azure入门教学系列 全面更新啦!
    与Advanced Telemetry创始人兼 CTO, Tom Naylor的访谈
    Windows Azure AppFabric概述
    Windows Azure Extra Small Instances Public Beta版本发布
    DataMarket 一月内容更新
    和Steve, Wade 一起学习如何使用Windows Azure Startup Tasks
    现实世界的Windows Azure:与eCraft的 Nicklas Andersson(CTO),Peter Löfgren(项目经理)以及Jörgen Westerling(CCO)的访谈
    正确使用Windows Azure 中的VM Role
  • 原文地址:https://www.cnblogs.com/tiger-xc/p/4865124.html
Copyright © 2011-2022 走看看