我所理解的monad(2):fold与monoid

在Scala中的核心数据结构List中,定义了fold操作,实际上这些方法是定义在scala集合库的最顶层特质GenTraversableOnce中的:

List中的左折叠(借用wiki上的图):

def foldLeft[B](z: B)(op: (B, A) => B): B

从图中可以看到,左折叠是用一个初始元素z从List的左边第一个元素开始操作,一直到对所有的元素都操作完。

现在我们对一个List进行累加操作:

scala> List("A","B","C").foldLeft("")(_+_)
res5: String = ABC

上面foldLeft传入的两个参数空字符串,以及二元操作函数 _+_ 不正好符合字符串monoid的定义吗?

object StringMonoid extends Monoid[String] {
    def append(a: String, b: String) = a + b
    def zero = ""
}

StringMonoid来代入:

scala> List("A","B","C").foldLeft(StringMonoid.zero)(StringMonoid.append)
res7: String = ABC

现在我们对List定义一个累加其元素的方法:

scala> def acc[T](list: List[T], m: Monoid[T]) = {
    list.foldLeft(m.zero)(m.append)
}

再进一步,把第二个参数改为隐式参数

scala> def acc[T](list: List[T])(implicit m: Monoid[T]) = { 
    list.foldLeft(m.zero)(m.append) 
}

现在Monoid成了一个type class,我们还可以再简化写法,用上下文绑定:

scala> def acc[T: Monoid](list: List[T]) = {
    val m = implicitly[Monoid[T]]
    list.foldLeft(m.zero)(m.append) 
}

如果我们在上下文提供了对应隐式值,就等于对List有了这种累加的能力:

scala> implicit val intMonoid = new Monoid[Int] { 
            def append(a: Int, b: Int) = a + b
            def zero = 0 
        }


scala> implicit val strMonoid = new Monoid[String] { 
            def append(a: String, b: String) = a + b
            def zero = ""
        }

scala> acc(List(1,2,3))
res10: Int = 6

scala> acc(List("A","B","C"))
res11: String = ABC

现在我们把Monoid看成基于二元操作(且提供单位元)的计算能力的抽象,不过仅仅是fold操作的话,还看不出它有什么威力。Monoid/SemiGroup中的结合律(associativity)特性才是它的威力所在,这个特性使得并行运算变得容易。

我所理解的monad(2):fold与monoid》上有4条评论

  1. Pingback引用通告: 我所理解的monad(3):半群(semigroup)与并行运算 | 在路上

  2. 廖师虎

    写得很好,通俗易懂。
    不过在有
    implicit val intMonoid = new Monoid[Int] {
    def append(a: Int, b: Int) = a + b
    def zero = 0
    }
    的情况下,
    def acc[T: Monoid](list: List[T]) = {
    val m = implicitly[Monoid[T]]
    list.foldLeft(m.zero)(m.append)
    }
    提示:could not find implicit value for parameter e: SemiGroupApp.Monoid[T]
    https://gist.github.com/lshoo/49ac78a4d3092c9e1eea

    回复
  3. Sheila

    impressive: “Monoid/SemiGroup中的结合律(associativity)特性才是它的威力所在,这个特性使得并行运算变得容易”

    回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注