快速了解函数式编程

简介: 快速了解函数式编程

说明

【跟月影学可视化】加餐篇学习笔记。



两种编程范式:命令式与声明式


编程范式有两种,分别是命令式(Imperative)声明式(Declarative)


  • 命令式:强调做的步骤也就是怎么做
  • 声明式:强调做什么本身,以及做的结果。

编程语言也可以分成命令式和声明式两种类型


  • 命令式:可以分成过程式和面向对象
  • 声明式:可以分成逻辑式和函数式



7ee97d1d4e95478b8e0f8ad86938800c.png


JavaScript 同时拥有命令式和声明式的特征

命令式的实现代码:

let kaimo = [1, 2, 3, 4, 5, 6];
let tempArr = [];
for(let i = 0; i < kaimo.length; i++){
  tempArr.push(kaimo[i] * 2);
}



声明式的实现代码:

let kaimo = [1, 2, 3, 4, 5, 6];
const double = x => x * 2;
kaimo.map(double);



函数式与纯函数


函数是对过程的封装,但函数的实现本身可能依赖外部环境,或者有副作用(Side-effect)。所谓函数的副作用,是指函数执行本身对外部环境的改变。我们把不依赖外部环境和没有副作用的函数叫做纯函数,依赖外部环境或有副作用的函数叫做非纯函数


例子1:add 是一个纯函数,它的返回结果只依赖于输入的参数,与调用的次数、次序、时机等等均无关。

function add(x, y) {
  return x + y;
}


例子2:getId 是一个非纯函数,它的返回值除了依赖于参数 id,还和外部环境(文档的 DOM 结构)有关。

function getId(id) {
  return document.getElementById(id);
}


例子3:join 也是一个非纯函数,它的副作用是会改变输入参数对象本身的内容。

funciton join(arr1, arr2) {
  arr1.push(...arr2);
  return arr1;
}



纯函数的优点

  • 易于测试:纯函数不需要依赖外部环境

可并行计算(时序无关)

   在浏览器中,可以利用 Worker 来并行执行多个纯函数

   在 Node.js 中,可以用 Cluster 来实现同样的并行执行

   使用 WebGL 的时候,纯函数有时候还可以转换为 Shader 代码,利用 GPU 的特性来进行计算。

有良好的 Bug 自限性:纯函数不会依赖和改变外部环境,所以它产生的 Bug 不会扩散到系统的其他部分




函数式编程范式与纯函数


尽可能多设计纯函数,少设计非纯函数,这样能够提升系统的可测试性、性能优化空间以及系统的可维护性。


如何让系统的纯函数尽可能多,非纯函数尽可能少呢?


答案是用函数式编程范式


例子:实现一个模块,用它来操作 DOM 中列表元素,改变元素的文字颜色


// 设置一个 DOM 元素的文字颜色
function setColor(el, color){
  el.style.color = color;
}
// 批量设置
function setColors(els, color){
  els.forEach(el => setColor(el, color));
}


上面两个函数都改变了外部环境(DOM)所以它们是两个非纯函数。

下面实现一个 batch 高阶函数 (High Order Function)来优化。

所谓高阶函数,是指输入参数是函数,或者返回值是函数的函数。

function batch(fn){
  return function(target, ...args){
    if(target.length >= 0){
      return Array.from(target).map(item => fn.apply(this, [item, ...args]));
    }else{
      return fn.apply(this, [target, ...args]);
    }
  }
}


这样模块就可以减少为一个非纯函数。

function setColor(el, color){
  el.style.color = color;
}
let setColors = batch(setColor);


高阶函数与函数装饰器


如果输入参数和返回值都是函数,这样的高阶函数又叫做函数装饰器(Function Decorators)


当一个高阶函数是用来修饰函数本身的,它就是函数装饰器。也就是说,它是在原始函数上增加了某些带有辅助功能的函数。


例子:实现一个通用的函数装饰器:当代码库要进行大版本升级,在未来最新的版本中我们想要废弃掉某些 API,需要做一个平缓过渡,给它们增加一个提示信息,告诉调用它们的用户,这些 API 将会在下一次升级中被废弃。


function deprecate(fn, oldApi, newApi) {
  const message = `The ${oldApi} is deprecated. Please use the ${newApi} instead.`;
  return function(...args) {
    console.warn(message);
    return fn.apply(this, args);
  }
}


在模块导出 API 的时候,对需要废弃的方法统一应用这个装饰器,就可以无侵入地修改模块的 API:

// 引入要废弃的 API
import {foo, bar} from './foo';
...
// 用高阶函数修饰
const _foo = deprecate(foo, 'foo', 'newFoo');
const _bar = deprecate(bar, 'bar', 'newBar');
// 重新导出修饰过的API
export {
  foo: _foo,
  bar: _bar,
  ...
}
目录
相关文章
|
程序员 Swift 开发者
26 函数式编程
函数式编程
69 0
|
4月前
|
机器学习/深度学习 数据采集 人工智能
函数式编程的实际应用
【10月更文挑战第12天】 函数式编程作为一种编程范式,在数据处理、金融、科学计算、Web 开发、游戏开发、物联网、人工智能等多个领域有着广泛应用。本文通过具体案例,详细介绍了函数式编程在这些领域的实际应用,展示了其在提高效率、确保准确性、增强可维护性等方面的显著优势。
189 60
|
3月前
|
数据采集 并行计算 算法
函数式编程
函数式编程是一种编程范式,它将计算视为数学函数的求值,并避免改变状态和可变数据。其核心思想是使用纯函数,减少副作用,提高代码的可读性和并行处理能力。
|
3月前
|
SQL 前端开发 测试技术
对函数式编程的深入理解
【10月更文挑战第25天】函数式编程提供了一种不同的编程思维方式,具有诸多优点,如提高代码质量、便于并发和并行编程、易于测试等。然而,它也存在一些局限性,需要根据具体的项目需求和场景来选择是否采用。随着对函数式编程的理解和应用的深入,它在现代软件开发中扮演着越来越重要的角色,为开发者提供了更多的编程选择和可能性。
28 1
|
4月前
|
并行计算 安全 数据处理
函数式编程和面向对象编程有什么区别?
【10月更文挑战第12天】 函数式编程与面向对象编程是两种不同的编程范式。前者强调数学函数的求值、不可变数据和纯函数,后者则以对象为核心,封装数据和方法。函数式编程更关注数据转换和计算过程,而面向对象编程关注对象的定义和交互。两者在数据处理、函数角色、代码结构、并发处理、灵活性和适用场景等方面存在显著差异。在实际开发中,可以根据需求选择合适的编程范式或结合使用。
180 4
|
6月前
|
自然语言处理 并行计算 大数据
什么是函数式编程
【8月更文挑战第2天】什么是函数式编程
142 13
|
Oracle JavaScript Java
函数式编程与Lambda表达式
函数式编程与Lambda表达式
|
安全 Java 数据库
Lambda表达式和函数式编程
Lambda表达式和函数式编程
201 4
Lambda表达式和函数式编程
|
Scala 索引 Python
第5章 函数式编程
第5章 函数式编程
534 0
第5章 函数式编程