zoukankan      html  css  js  c++  java
  • 什么是函数式编程

    导读

    建议先阅读一下这几篇博客:
    函数式编程初探
    函数式编程入门教程
    图解 Monad

    什么是函数式编程

    函数式编程中的函数指的并不是编程语言中的函数(或方法),它指的是数学意义上的函数,即映射关系(如:y = f(x)),就是 y 和 x 的对应关系。

    数学上对于函数的定义是这样的:“给定一个数集 A,假设其中的元素为 x。现对 A 中的元素 x 施加对应法则 f,记作 f(x),得到另一数集 B。假设 B 中的元素为 y。”

    所以当我们在讨论“函数式”时,我们其实是在说“像数学函数那样,接收一个或多个输入,生成一个或多个结果,并且没有副作用(稍后解释)”

    实际上当一个函数无论在何处、何时调用,如果使用相同的输入总能持续地得到相同的结果,我们就可以说他具备了函数式的特征。

    需要注意的是,纯函数式编程语言中的变量指的也不是命令式编程语言中的变量(即储存状态的单元),而是代数的变量,表示一个未知的数,或者一个可带入函数的值。
    (在代数中对于“数本身是什么”这样的问题并不关心,它关心是各种关系及其性质)

    所以函数式编程语言中所说的变量只是一个变量的名称,变量的值是不可变的(immutable),也就是说不允许像命令式编程语言中那样多次给一个变量赋值。
    (PS:我们真的需要变量吗?

    函数是语言中的变量:

    • 变量的值一旦绑定就不再发生变化
    • 变量中存放的与其说是指,不如说是表达式(惰性求值)

    函数式编程语言的特性

    在了解了函数编程语言的基本概念之后,我们再来看一下函数式编程语言所具备一些特性,理解这些特性将有助于我们更好的理解“什么是函数式编程”。

    一,函数是一等公民

    函数是一等公民,它的意思就是函数与其他数据类型一样,可以把它们存在数组里,当做参数传递,赋值给变量,可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,也可以对函数进行组合。

    二,高阶函数

    在函数式编程中,如果函数能满足下面任一要求就可以被称为高阶函数(higher-order function):

    • 接受至少一个函数作为参数
    • 返回的结果是一个函数

    三,柯里化

    所谓“柯里化” ,就是把一个多参数的函数 f,转换为单参数函数 g,并且这个函数的返回值也是一个函数。

    // 柯里化之前
    function add(x, y) {
      return x + y;
    }
    add(1, 2) // 3
    // 柯里化之后
    function addX(y) {
      return function (x) {
        return x + y;
      };
    }
    addX(2)(1) // 3
    

    四,副作用

    所谓“副作用”,指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。
    在像 C++ 这样的命令式语言中,函数的意义与数学函数完全不同。例如,假设我们有一个 C++ 函数,它接受一个浮点参数并返回一个浮点结果。从表面上看它可能看起来有点像数学函数意义上的映射实数成实数,但是 C++ 函数可以做的不仅仅是返回一个取决于其参数的数字,它还可以读写其他的全局变量,也可将将输出写入屏幕并接收来自用户的输入。但是,在纯函数式语言中,函数只能读取其参数提供给它的内容,并且它对世界产生影响的唯一方式就是通过它返回的值。

    五,纯函数

    纯函数编程和函数编程的区别在于:是否允许在函数内部执行一些非函数式的操作,同时这些操作是否会暴露给系统中的其他地方?也就是是否存在副作用。如果不存在副作用,或者说可以不用在意这些副作用,那么就将其称为纯粹的函数式编程。

    六、引用透明性

    函数无论在何处、何时调用,如果使用相同的输入总能持续地得到相同的结果,就具备了函数式的特征。这种不依赖外部变量或“状态”,只依赖输入的参数的特性就被称为引用透明性(referential transparency)。
    “没有可感知的副作用”(比如不改变对调用者可见的变量,进行I/O,不抛出异常等)的这些限制都隐含着引用透明性

    五,递归和迭代

    对于函数式而言,循环体有一个无法避免的副作用,就是它会修改某些对象的状态,通常这些对象又是和其他部分共享的。而且也因为变量值是不可变的,纯函数编程语言也无法实现循环。
    所以纯函数编程语言通常不包含像 while 和 for 这样的迭代构造器,而是采用的无需修改的递归。
    下面是一段标准的基于循环的方式来计算阶乘:

    static int factorialIterative(int n){
        int r = 1;
        for (int i = 1; i <= n; i++) {
            r *= 1;
        }
        return r;
    }
    

    而采用递归形式的方式如下:

    static long factorialRecursive(long n){
        return n == 1 ? 1 : n * factorialRecursive(n-1)
    }
    

    扩展阅读
    http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html
    https://www.zhihu.com/question/28292740
    https://blog.csdn.net/qq_31179577/article/details/78745248

  • 相关阅读:
    JavaScript数组去重(12种方法,史上最全转载)
    数组从大到小排序的两种方式
    jquery.inArray()和splice()使用小记
    HbuilderX 线上打包Android9.0版本无法更新
    常见的移动端H5页面开发遇到的坑和解决办法
    监听点击物理返回键及mui.fire父子页面传参
    深入理解DOM事件类型系列第三篇——变动事件
    jquery监听div等元素的内容变化
    寒假进度3
    寒假进度2
  • 原文地址:https://www.cnblogs.com/liyutian/p/10036901.html
Copyright © 2011-2022 走看看