Monad到底为程序设计带来啥好处

OOP 程序员到 FP 程序员 . Monad 到底为程序设计带来啥好处 .

1.      计算电阻的例子

Programming F# 242 页有一个计算电阻的例子 , 是用 F# computation expression 实现的 , 我会用 Scala 再实现一次 .


并列放置的电阻的阻抗满足关系 : 1/R= 1/R1 + 1/R2 + …+1/Rn. 为了简便起见 , 这里只考虑 3 个电阻的情况 .

2.      先写个计算函数 :

  def calculateResistance0(r1: Float ,r2: Float ,r3: Float ) = {

        val v1 = 1/r1

      val v2 = 1/r2

      val v3 = 1/r3

      val v = 1/(v1+ v2+v3)

     

}

调用函数 :

      val r = calculateResistance0(10, 20, 30) //return 5.4545455

当三个电阻中有一个 阻抗为零时 , r1 =0, 1/r1 返回 Infinity 表示无穷大 . 那么 :

      val r = calculateResistance0(0, 20, 30) // return 0.0

calculateResistance0 能处理各种情况 , 得到的结果没有问题 . 问题是 calculateResistance0 是最优的解吗 ? 为啥当 r1=0 , 我们还要去计算 1/r2, 1/r3 ? 因为无穷大加任何数结果还是无穷大 , 无穷大的倒数一定是 0. 如果除法操作是一个很耗资源的操作 , 减少不必要的操作会提高 calculateResistance0 函数的效率

3.    改进 calculateResistance0:

  def calculateResistance1(r1: Float ,r2: Float ,r3: Float ) = {

      if (r1==0) 0 else

       {   val v1 = 1/r1

         if (r2 == 0) 0 else {

             val v2 = 1/r2

             if (r3==0) 0 else {

                val v3 = 1/r3

                val v = 1/(v1+v2+v3)

                v

             }

         }

       }

  }

调用函数 :

val r = calculateResistance1(10, 20, 30) //return 5.4545455

val r = calculateResistance1(0, 20, 30) // return 0.0

结果正确 , 效率也提高了 .( r3 为零而 r1,r2 不为零时 , 效率退化为 calculateResistance0). 但是 , 程序结构太丑陋了 . 严重影响观感 . 有没有办法让程序写得象 calculateResistance0 一样 , 效率象 calculateResistance1 一样呢 ?

 

4.      题外话

如果有同学说下面的代码效率最高 , 我同意 , 但它不是我要说明的主题 . 本文想要解决的问题是 , 再强调一遍 : 让程序写得象 calculateResistance0 一样 , 效率象 calculateResistance1 一样

  def calculateResistance2(r1: Float ,r2: Float ,r3: Float ) = {

      if (r1==0||r2==0||r3==0) 0.0

      else

           1/(1/r1+1/r2+1/r3)

  }

 

5.      Monad 闪亮登场

仿照 scala Option 类写了个 M. 为了突出重点 , 剔除了没有关联的其他函数和 properties

abstract class M[+A]{

    def get:A

    def isEmpty: Boolean

    def flatMap[B](f: A => M[B]):M[B] = {

       if (isEmpty) Failure else f( this .get)

    }

    def map[B](f: A => B):M[B]= {

       if (isEmpty) Failure else Success(f( this .get))

    }

}

case object Failure extends M[ Nothing ]{

  def isEmpty = true

  def get = throw new NoSuchElementException( "None.get" )

}

case class Success[+A](x:A) extends M[A]{

    def isEmpty = false

    def get = x

}

M 代表一个 Monad.

再写一个除法 object:

object Div {

    def apply(i: Float , j: Float ) = {

       println( "called i=" +i+ ";j=" +j); // 监视是否调用本段代码

       if (j==0) Failure else Success(i/j)}

}

Monad 的工作原理有时间再写 .

6.      重写 calculateResistance0

  def calculateResistance3(r1: Float ,r2: Float ,r3: Float ) = {

      for {

           m1 <- Div(1,r1)

           m2 <- Div(1,r2)

           m3 <- Div(1,r3)

      } yield (1/(m1+m2+m3))

  }

调用函数 :

val r = calculateResistance3(10, 20, 30) //return Success(5.4545455)

val r = calculateResistance3(0, 20, 30) // return Failure

calculateResistance3 calculateResistance0 写法相似 , 效率和 calculateResistance1 一样 .

 

7.      结语

本文主要目的是说明用 Monad 来作什么 , 没有讨论 Monad 的工作原理 ( 网上有很多文章有介绍 ). 希望对大家理解 Monad 有帮助 .

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值