初见函数式编程

栏目: 编程语言 · 发布时间: 5年前

内容简介:首先,我们放下编程的概念,我们来看函数。函数的概念来自于数学,数学中的函数然而在编程中,函数并不具有这个特点,举个栗子:

首先,我们放下编程的概念,我们来看函数。

函数的概念来自于数学,数学中的函数 f(x) = y 有一个非常重要的特点 对于一个给定的 x,有唯一的 y 与其对应 (这就是为什么椭圆曲线不是函数)

然而在编程中,函数并不具有这个特点,举个栗子:

let val = 1
function add(x){
    return x + val
}
console.log(add(1)) // 2
val += 1
console.log(add(1)) // 3
复制代码

可以看到编程中的函数在参数相同的情况下,允许有不同的返回值——只要依赖于函数外部的量

那么当函数依赖于外部的变量(常量不会有这样的问题),并且函数在多处调用,就有可能出现 bug,函数的调用结果可能会和预期结果相去甚远

函数式编程 就要求我们规避这样的情况,让所有函数对于相同的输入的返回值相同,这样的特性就叫做引用透明性,这就是函数式编程的核心特性!

一个符合引用透明性的函数的栗子:

function id(x){ return x; }
复制代码

函数式编程带来的优点

在上文中提到过,编程语言中的函数大多是不满足数学中的函数的概念的,so 我们将满足数学函数条件的函数称为“纯函数”

函数式编程的优点大多都来自于纯函数

可测试性

除了测试人员进行的全方位测试外,我们在开发过程中往往要对自己写的代码进行模块测试

在开发中,我受非纯函数迫害已久,由于它依赖了外部变量(比如存储在 localStorage 中的数据)我不得不三番五次去检查这些外部变量是否在某个过程中被改变甚至是删除

let val = 1
function add(x){
    return x + val
}
console.log(add(1)) // 2
// 在未知因素影响下 val被改变
console.log(add(1)) // 预期结果 2,实际输出 emmmmm
复制代码

如果我没有注意外部依赖而是一头扎进函数逻辑里,可能永远都找不到这个bug

代码的并发性

虽然我们都知道 JS 是一门单线程语言(关于JS的执行可以参见-> 技术总结——JS的执行顺序 ),但是我们为了提高前端的性能可能会通过 WebWorker 来并发执行多个任务,或者在 Node 环境下 JS 并发执行函数

这个时候就是对非纯函数的一个很大的考验:

let global = "全局变量"
let func1 = ()=>{
    global = "全局变量被改变了"
    // 一些逻辑
}
let func2 = ()=>{
    if(global = "全局变量"){
        return true
    }
}
复制代码

上面的两个函数都依赖于外部的global,当它们并发执行,func1就会对func2产生影响,如果将它们变为纯函数就不会有这样的问题:

let global = "全局变量"
let func1 = (x)=>{
    x = "全局变量被改变了"
    // 一些逻辑
}
let func2 = (x)=>{
    if(x = "全局变量"){
        return true
    }
}
复制代码

函数执行的缓存

当我们的函数都是纯函数,而我们又会多次调用函数,我们就可以对函数对象进行一个缓存

比如我们需要大量计算数字的4次方,我们可以建立一个映射表用来缓存函数的执行结果:

// 映射表
let fourTimesTable = {};
let fourTimes = (x){
    return x*x*x*x
}
// 检查表中是否有 2 的四次方,如果有就返回,如果没有就执行函数避免运算
fourTimesTable.hasOwnProperty(2)?
    fourTimesTable[2]:
    fourTimesTable[2] = fourTimes(2)
复制代码

管道与组合

管道过滤器是一种很经典的设计模式,我们可以将这种模式和函数式编程结合起来

在管道和过滤器软件体系结构中,每个模块都有一组输入和一组输出。每个模块从它的输入端接收输入数据流,在其内部经过处理后,按照标准的顺序,将结果数据流送到输出端,以达到传递一组完整的计算结果实例的目的。

在这种结构中,各模块之间的连接器充当了数据流的导管,将一个过滤器的输出传到下一个过滤器的输入端。所以,这种连接器称为“管道”。

我们也可以将这样的私用运用在函数式编程中,将一个个函数作为过滤器,通过函数的组合,形成一条数据处理的通路:

// 参数累加
function add(...args){
    let result = args.reduce((prev, cur, index, arr)=> {
        return prev + cur;
    })
    return result
}
// 参数累乘
function times(...args){
    let result = args.reduce((prev, cur, index, arr)=> {
        return prev * cur;
    })
    return result
}

let arr1=[1,3,6],arr2=[2,5,21],arr3=[3,7,8,27,4]
// 三组数据,要求组内累乘,然后结果累加
add(times(...arr1),times(...arr2),times(...arr3))
// 三组数据,要求组内累加,然后结果累乘
times(add(...arr1),add(...arr2),add(...arr3))
复制代码

对函数式编程的初探暂止于此,进一步学习后再做总结~

本文同步发布于我的个人博客, CSDN掘金

如果有什么问题,意见,建议欢迎评论;如果觉得我写的不错,那就点个赞吧~


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

GitHub入门与实践

GitHub入门与实践

[日] 大塚弘记 / 支鹏浩、刘斌 / 人民邮电出版社 / 2015-7 / 39.00元

本书从Git的基本知识和操作方法入手,详细介绍了GitHub的各种功能,GitHub与其他工具或服务的协作,使用GitHub的开发流程以及如何将GitHub引入到企业中。在讲解GitHub的代表功能Pull Request时,本书专门搭建了供各位读者实践的仓库,邀请各位读者进行Pull Request并共同维护。一起来看看 《GitHub入门与实践》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具