内容简介:个人觉得这个定义肥肠生动形象了。函数式编程处处体现着数学的逻辑思想,通过减少外部依赖来避免外部变量的改变对于程式内部状态的改变,使得程序变得简易、声明式、易于维护。这里推荐一下F大的《JS函数式编程指南》,系统介绍了函数式编程的思想。
JS函数式编程思想
- 维基百科定义:函数式编程(英语:functional programming),又称泛函编程,是一种编程范式,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。
个人觉得这个定义肥肠生动形象了。
函数式编程处处体现着数学的逻辑思想,通过减少外部依赖来避免外部变量的改变对于程式内部状态的改变,使得程序变得简易、声明式、易于维护。
这里推荐一下F大的《JS函数式编程指南》,系统介绍了函数式编程的思想。
中文版电子书下载地址走你: https://legacy.gitbook.com/bo...
本文章是自己阅读后的一些理解和记录~
First Class Funtcion
这个概念同样出于书中。规定了变量可以取的值得范围,以及该类型的值可以进行的操作。根据类型的值的可赋值状况,可以把类型分为三类。
- 一级的(first class)。该等级类型的值可以传给子程序作为参数,可以从子程序里返回,可以赋给变量。大多数程序设计语言里,整型、字符类型等简单类型都是一级的。
- 二级的(second class)。该等级类型的值可以传给子程序作为参数,但是不能从子程序里返回,也不能赋给变量。
- 三级的(third class)。该等级类型的值连作为参数传递也不行。
first class function
PS:scala 是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。
因此,function作为变量的一种,可以被储存,当作参数传递,或者被复制给变量等等...
Pure Function
首先弄清纯函数的概念: Pure function
意指相同的輸入,永遠會得到相同的輸出,而且沒有任何顯著的副作用。
它符合两个条件:
- 此函数在相同的输入值时,总是产生相同的输出。函数的输出和当前运行环境的上下文状态无关。
- 此函数运行过程不影响运行环境,也就是无副作用(如触发事件、发起http请求、打印/log等)。
简单来说,也就是当一个函数的输出不受外部环境影响,同时也不影响外部环境时,该函数就是纯函数,也就是它只关注逻辑运算和数学运算,同一个输入总得到同一个输出。
比如 slice
和 splice
,但是 slice
是纯函数,而 splice
不是,因为后者会改变源数据。
这就是强调使用纯函数的原因,因为纯函数相对于非纯函数来说,在可缓存性、可移植性、可测试性以及并行计算方面都有着巨大的优势。
看一下书中的例子:
// impure var minimum = 21; var checkAge = function(age) { return age >= minimum; }; // pure var checkAge = function(age) { var minimum = 21; return age >= minimum; };
在impure 的版本中,checkAge 的結果將取決於 minimum 這個变量,换句话说,除了输入以外,它将取决于系统状态,一旦引用了外部环境,它就不符合pure了。
当然,也可以直接冻结变量,使得状态不再变化,那么它也是一个纯函数:
var immutableState = Object.freeze({ minimum: 21, });
Curry(柯里化)
书中定义柯里化的概念是:你可以只透過部分的參數呼叫一個 function,它會回傳一個 function 去處理剩下的參數。
函数柯里化是一种“预加载”函数的能力,通过传递一到两个参数调用函数,就能得到一个记住了这些参数的新函数。从某种意义上来讲,这是一种对参数的缓存,是一种非常高效的编写函数的方法,将一个低阶函数转换为高阶函数的过程就叫柯里化。
//举个栗子 var checkage = min => (age => age > min); var checkage18 = checkage(18); checkage18(20); // =>true var curry = require('lodash').curry; //柯里化两个纯函数 var match = curry((what, str) => str.match(what)); var filter = curry((f, ary) => ary.filter(f)); //判断字符串里有没有空格 var hasSpaces = match(/\s+/g); hasSpaces("hello world"); // [ ' ' ] hasSpaces("spaceless"); // null var findSpaces = filter(hasSpaces); findSpaces(["tori_spelling", "tori amos"]); // ["tori amos"]
Compose—Functional饲养
假设要对某个字符串做一系列操作,我们做的事情一多,嵌套的层数会非常深。类似于巢状堆积(比如c(b(a())))这种堆砌方式非常不直观,当我们希望代码以平行方式(书中成为左倾)组合执行时,就成为 Compose
compose 的概念直接來自於數學課本,所以compose都有的一个特性:
// 結合律(associativity) var associative = compose(f, compose(g, h)) == compose(compose(f, g), h); // true
PointFree
pointfree 模式指的是,永远不必说出你的数据。它的意思是说,函数无须提及将要操作的数据是什么样的
// 非 pointfree,因為我們提到資料:word var snakeCase = function(word) { return word.toLowerCase().replace(/\s+/ig, '_'); }; // pointfree var snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase);
这种风格能够帮助我们减少不必要的命名,让代码保持简洁和通用。
当然,为了在一些函数中写出Point Free的风格,在代码的其它地方必然是不那么Point Free的,这个地方需要自己取舍。
声明式与宣告式代码
// 命令式 var makes = []; for (var i = 0; i < cars.length; i++) { makes.push(cars[i].make); } // 宣告式 var makes = cars.map(function(car) { return car.make; });
命令式代码:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。
声明式代码:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。
命令式的循环要求你必须先实例化一个数组,而且执行完这个实例化语句之后,解释器才继续执行后面的代码。然后再直接迭代 cars 列表,手动增加计数器,就像你开了一辆零部件全部暴露在外的汽车一样。这不是优雅的 程序员 应该做的。
声明式的写法是一个表达式,如何进行计数器迭代,返回的数组如何收集,这些细节都隐藏了起来。它指明的是做什么,而不是怎么做。除了更加清晰和简洁之外,map 函数还可以进一步独立优化,甚至用解释器内置的速度极快的 map 函数,这么一来我们主要的业务代码就无须改动了
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。