第一章 什么是函数式编程

第一章 什么是函数式编程

一.前言

  1. 函数式编程:使用纯函数来构造程序。
    (1)纯函数是没有副作用的函数。
    (2)副作用是指,函数执行过后,不仅有返回值,与此同时,还进行了其他操作:
        (a) 修改了其他变量的值
        (b) 设置了对象的属性
        (c) 抛出了一个异常,或以一个错误终止
        (d) 打印到终端,或获取用户键盘输入
        (e) 读取或写入一个文件
  2. 纯函数的优势
    (1)纯函数是模块化的,容易被测试,复用,并行化,泛华,推导。因此纯函数降低了bug出生的概率
    (2)纯函数限制了怎样写程序,而非限制表达什么样的程序。就是说,纯函数依然能进行IO操作,循环改变变量等

二. 函数式程序对副作用的改造

  1. 第一个程序
    class Cafe{
      def buyCoffee(cc:CreditCard) : Coffee = {
        val cup = new Coffee()
        cc.charge(cup.price)   // 这段代码出现副作用:
        cup 
      }
    }
    ```
(1)函数只是为了返回一个Coffee对象,而在此过程中,却涉及到了CreditCard对象的更改。因此产生副作用,这个副作用使得代码难以测试,因为信用卡计费更改操作可能是一个webservice,而我们在测试中并不真的想联系信用卡公司
(2)因此,我们传递一个Payments对象给函数,使代码更加模块化

    


2. 第2个程序
    ```
    class Cafe{
      def buyCoffee(cc:CreditCard,p:Payments) : Coffee = {
        val cup = new Coffee()
        p.charge(cc,cup.price)   // 这段代码出现副作用:
        cup 
      }
    }
    ```
(1)改进后的优点:虽然payments的实现可能还是需要调用远程webservice,但是测试代码时,可以使用mock创造一个不用远程调用的接口
(2)仍然存在的缺点:如果想买12杯咖啡,需要对函数循环12次,这样会进入12次计费计算过程,会导致效率急剧下降。;


3. 函数式的代码写法
    ```
    class Cafe{
      def buyCoffee(cc:CreditCard) : (Coffee,Charge) = {
        val cup = new Coffee()
        // p.charge(cc,cup.price)   删除这段
        (cup,Charge(cc,cup.price)) 
      }
      def buyCoffees(cc:CreditCard,n:Int):(List[Coffee],Charge) = {
        val purchases:List[(Coffee,Charge)] = List.fill(n)(buyCoffee(cc))
        val (coffees,charges) = purchases.unzip(coffees,charges.reduce( (c1,c2)=>c1.add(c2) ))
      }
    }
    
    case class Charge(cc:CreditCard,amount:Double){
      def add(other:Charge):Charge = {  // 累计计费,只能累计一个卡
        if(cc==other.cc)
          Charge(cc,amount+other.amount)
        else
          throw new Exception("can't add different cards")
      }
    }
    ```
【注】:函数式程序的标准写法:纯内核 + 很薄的外壳


####三. 纯函数究竟是什么
1. `引用透明`:所谓引用透明的概念,就是指一个表达式,这个表达式在程序中可以被他的结果所取代,而不改变程序的含义
2. `纯函数`:我们利用引用透明的概念定义了纯函数:
              假设存在一个函数$f$和一个引用透明的表达式$x$,若表达式$f(x)$对所有引用透明的表达式$x$也是引用透明的,那么$f$是一个纯函数
3. 非纯函数,往往会破坏`引用透明`,使同一个表达式产生的结果并不相同,使人费解
(eg:1+1表达式永远为2,这就是引用透明,但有些java函数,并不是引用透明的,例如stringbuilder的相关方法)

    ```scala
    scala> val x = new StringBuilder("Hello")
    x: StringBuilder = Hello
    
    scala> val r1 = x.append(" world")
    r1: StringBuilder = Hello world
    
    scala> val r2 = x.append(" world")   // r2和r1的表达式一样,但是得到的结果却不同
    r2: StringBuilder = Hello world world
    ```
原文地址:https://www.cnblogs.com/moonlord/p/6273141.html