Functional Thinking

到底什么是函数式编程

Posted by BY annie dong on September 10, 2018

从命令式编程,到面向对象风靡,再到现在一个古老的编程范式-函数式编程的出现…


编程范式

范式 - 科学哲学概念,简单来说就是基本纲领,范式之间不可通约,范式转换是断裂性革命性转换

  • 命令式编程: 专注于”如何去做”,这样不管”做什么”,解决某一问题的具体算法实现
    1. 面向过程
      就是分析解决问题所需要的步骤,然后把这些步骤一步一步实现
    2. 面向对象
      所谓面向对象,就是将程序里面的模型看做一个一个的对象。对象和对象之间会产生彼此的联系。使用对象来解决问题
  • 声明式编程: 非命令式的编程都可归为声明式编程,专注于”做什么”而不是”如何去做”
    1. 函数式编程
    2. 逻辑式编程

lambda

函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)
在lambda 演算中,每个表达式都代表一个只有单独参数的函数,这个函数的参数本身也是一个只有单一参数的函数,同时,函数的值又是一个只有单一参数的函数。函数是通过 lambda 表达式匿名地定义的,这个表达式说明了此函数将对其参数进行什么操作。

函数式编程特点

  1. 函数是第一等公民
    那么相当于它支持了高阶函数,高阶函数就是至少满足下列一个条件 1.接受一个或多个函数作为输入 2.输出一个函数
  2. 纯函数
    函数的结果只依赖于输入的参数且与外部系统状态无关——只要输入相同,返回值总是不变的。
    除了返回值外,不修改程序的外部状态(比如全局变量、入参)。——满足这个条件也被称作“没有副作用 (side effect)”
    如果一个表达式,对于相同的输入,总是有相同的结果并且不修改程序其他部分的状态,那么这个表达式是引用透明的。
    F(x) = ax +b
  3. 不变性
    =: 不是指派,而是模式比對
    把等號左邊的模式,跟右邊的值比對看看。如果有辦法找到讓等式成立的情況,那就幫你綁定變數。
    [a, a, b] = [1, 2, 3]
    #=> ** (MatchError) no match of right hand side value: [1,2,3]
    

    a不可能又是1,又是2

    [a, a, b] = [1, 1, 3]
    # => [1, 1, 3]
    
  4. 尾递归优化 如果递归很深的话,stack受不了,并会导致性能大幅度下降。尾递归优化技术——每次递归时都会重用stack,这样一来能够提升性能,当然,这需要语言或编译器的支持。Python就不支持

一等公民的纯函数带来了什么

  1. 可以利用Memoization技术提升性能
    满足引用透明的表达式(包括任意纯函数调用)满足这样一个特点,就是任意两次调用只要输入相同,其结果总是不变的。于是可以将第一次的计算结果缓存起来,遇到下一次执行时直接替换,依然能保证程序的正确性。这种优化方法称为Memoization
  2. 延迟求值 (Lazy Evaluation)
    延迟求值是指表达式不在它被绑定到变量时就立即求值,而是在该值被用到的时候才计算求值
    很显然,延迟求值的正确性需要纯函数的性质来保证——即在输入参数相同的情况下,无论什么时候被执行,结果总是不变的
  3. 可以使用 currying 技术进行函数封装
    curryiny 将接受多个参数的函数变换成接受其中部分参数,并且返回接受余下参数的新函数。
    比如幂函数pow(x, y),它接受两个参数——x和y,计算x^y。使用currying技术,可以将y固定为2转化为只接受单一参数x的平方函数,或者将y固定为3转化为立方函数
  4. parallelization并行
    所谓并行的意思就是在并行环境下,各个线程之间不需要同步或互斥。

函数式和面向对象比较

面向对象核心是状态,函数式核心是数据 所以面向对象更适合对业务(复杂的状态变化)的设计,而函数式适合对功能(复杂的数据变化)的设计 随着面向对象设计方式的发展,理论是越来越完善,复杂度也越来越高,面向对象的设计方式很多时候不再把目光投向实际的问题, 而是追求所谓的设计技巧。 函数式编程则更加直接,将问题转化为对数据的处理,关注点更容易集中在问题本身

函数式和AI

函数式编程能够再度火起来,和AI也有一定的关系,机器学习本身就是对大量数据的学习和处理,通过数据来训练出算法。这种模式更加适合函数式编程,而面向对象面对这种未知结果的学习,抽象会非常困难。

java的函数式编程

java8提供了stream, optional 实现一个不可变的java类:

  1. 所有字段标记为final
  2. 类标记为final, 防止被子类覆盖
  3. 不要提供无参数构造器,至少提供一个构造器,除构造器外不提供任何改变状态的方法

参考资料