计划
- 先讲解一下为什么要使用范畴论的理论来写代码
- 把范畴论当中的和计算机相关的知识讲解一下
- 把2提到的理论应用一下
- 实践一下,拿起新武器提高我们的战斗力吧!
第一章 目的
写这系列文章的目的是什么?
当然是提高生产效率!!不为了这个谁会去发明那么多东西呢,难道仅仅是为了领一个谷歌的ACM银碗!
我们自打学习写作文开始就知道一个重要的主旨,不要跑题。哈哈,我皮了一下。所以我们那些熟知的DRY(不要重复自己)SR(单一职责)LCHC(高内聚低耦合)YAGNI(避免过度开发)PLS(最小意外原则)等原则不在讲解,但是我们的示例代码还是会遵守他们的。
第二章 命令式和声明式
1. 区别
首先我们来看一下二者在代码书写习惯上的区别。
示例意思是将一个数组的各个数字倍增,然后返回结果
//命令式示例
var num=[1,2,3,4,5];
var result=[];
for(var i=0;i<num.length;i++){
result.push(num[i]*2);
}
//声明式示例
var num=[1,2,3,4,5];
var result=num.map(x=>x*2);
看吧,这是个一行代码搞定一切的时代。
2.新的思考方式
命令式代码有点像一个强权的人在事无巨细的指挥某个活动的所有工作步骤,直到最后的工作结果。
声明式代码却不是这么霸道,他把他的倍增的需求告诉了计算机,计算机拿到需求就去执行了。
同样的结果,不一样的思考角度,造成了不一样的代码量。
如果抛弃代码执行速度的考量,仅仅是实现需求,我想大家肯定会被声明式的开发效率所折服了吧。写高效执行的代码的人还是不要看了。
如果读者不理解map方法也不要紧,毕竟这是范畴论里面提到的东西,后面我会为大家一一介绍这些范畴论在计算中的实现。
恩 …… 我好像又提到了一个新理论,读者们不要喷我啊,我的学习方式就是先引入理论,再实践。我感觉这样能学习是最快的方案。
第三章 函数
1. 函数
最早的计算机程序的概念是“数据结构+算法”,这个理念已经被提出很久了,也一直被计算机界奉为宪法(大部分人会说“奉为圣经”吧,我不喜欢这么描述)。我不是想推翻这个理论,毕竟这个理论是正确的。我只是想吐槽一下算法这个东西,现在有多少小白在写一些无用的弱智的难以维护的算法。看到这样的人的时候也只能叫他一句“码农”来鄙视一下了。
自计算机界引用了数学的函数概念之后,一切都变了,代码块第一次有了自己的意义,通过方法名称或者方法注释就可以知道它到底是为什么存在的了(注意,本文是在探讨一种新的提高效率的代码组织模式,请忽视乱起名字的,不写注释的小白代码)。
现在看一个示例
//加
var add = function(x, y) { return x + y };
//乘
var multiply = function(x, y) { return x * y };
可能你会说为什么不直接加或者直接乘呢?还要造一个方法来?这不是多此一举吗?我们这里只是个例子而已,读者不要着急喷,毕竟越简单越容易理解,太抽象了就会有人说我不接地气了。
回到正题来,上面的两个方法把加或者乘的这个算法封装进了一个方法,如果逻辑更复杂,这样的把算法封装起来(函数)的写法会更加方便了,因为谁也不想重复自己。
2. 纯函数
如果你有一丁点编程经验的话,想必这里可以跳过了。
先说说定义:纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。
再来个示例:
//非纯函数
var a=1;
var addA=function(b){a=a+b;return a;};
addA方法不是一个纯函数,它的结果依赖于a的值,因此我们不能明确的知道返回值是什么;除此之外,a的值还被改变了,在调用一次的结果还是不一样的。天哪,这里有两个麻烦点了。如果你干过测试这个工种,你肯定知道测试这种代码的难度了。
//纯函数
var add=function(a,b){return a+b;};
我们又一次用了上面的add方法,这次add方法的结果仅仅和他的输入参数相关了,我们能够明确的知道返回结果,并且可以很方便的做代码测试。——所谓的高内聚低耦合不就是这么个期望吗。
3. 高阶函数
高阶函数是指至少满足下列条件之一的函数:
1、函数可以作为参数被传递;
2、函数可以作为返回值输出。
光说不练假把式,来个实例看看。
//作为参数被传递
var calculate=function(a,b,metod){
return method(a,b);
};
var add=function(a,b){return a+b;};
console.info(calculate(1,2,add));//3
//作为返回值被输出
var say=function(){
return function(name){
return name+' hello !';
};
};
var sayHello=say();
console.info(sayHello("Tom"));
4. 柯里化
currying(函数柯里化),又称惰性执行(又称部分求值)。一个currying的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即执行,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要执行的时候,之前传入的所有参数才会被执行。
var addX=function(a){
return function(b){
return a+b;
};
};
var addOne=addX(1);
console.info(addOne(1));//2
console.info(addOne(2));//3
柯里化是个非常强大的利器,它应用的原理是:利用方法堆栈中的参数如果被引用,则保留被引用的参数。因此利用这个功能可以保留之前的输入,从而重复使用该输入。
在后面的实践中,我们会多次利用这个利器,实现更加强大功能!
5. 反柯里化
unCurrying(反柯里化)
反柯里化,是一个泛型化的过程。它使得被反柯里化的函数,可以接收更多参数。目的是创建一个更普适性的函数——可以被其它对象使用,而不仅仅局限于原对象。
//实现方案1
function unCurrying(fn){
return function(){
var args = [].slice.call(arguments);
var that = args.shift();
return fn.apply(that, args);
}
}
//实现方案2
Function.prototype.unCurrying = function(){
var self = this;
return function(){
return Function.prototype.call.apply(self, arguments);
}
}