写点什么

编程核心能力之组合

用户头像
顿晓
关注
发布于: 2020 年 07 月 04 日
编程核心能力之组合

​大家刚学编程时,都会看到这样的介绍:『数据结构+算法=程序』。



这个描述很学术,毕竟是图灵奖得主提出来的,学编程的人倒无所谓,但和外行交流时就不够通俗;也让编程初学者走了很多弯路,想想你花了多长时间才理解这句话的真谛?



今天推荐Eric Elliott的《Composing Software》,这本书里作者将软件比作组合的艺术,这个隐喻更贴切『软件工艺』,抽象度也更高,容易和其他事物联系起来,当然也更容易让人抓住问题的本质。



『编程日课』把『组合』做为编程的核心能力之一,将持续提供各种服务,帮助每个人都熟练掌握此能力。



编程的日常工作中随处可见组合的影子,依据『数据结构+算法=程序』从技术上把组合的对象分成2类:函数和数据。



今天先来两个函数的小例子,对比看下其中的差异,尝下好的组合方式的味道。



这个例子是最普通的『f after g』方式,也就是说,先计算g函数的值,然后将其当做参数传给f函数,最后取得f函数的值。



这个是细节最多的版本,当然,大家都会觉得很繁琐。



const g = n => n + 1;
const f = n => n * 2;
const doStuff = x => {
const afterG = g(x);
const afterF = f(afterG);
return afterF;
};
doStuff(20);//42



这个是最简化的版本,像数学函数一样。



const g = n => n + 1;
const f = n => n * 2;
const doStuffBetter = x => f(g(x));
doStuffBetter(20);//42



现在增加一个需求:打印出每个函数的返回值,包含用于计算的临时值。



细节最全的版本是这样的,在合适的地方就地修改,好处是改的时候方便,坏处是需求再变时,修改的成本只会增加不会减少。



const doStuff = x => {
const afterG = g(x);
console.log(`after g: ${ afterG }`);
const afterF = f(afterG);
console.log(`after f: ${ afterF }`);
return afterF;
}
doStuff(20);//=>
/*
"afterg:21"
"afterf:42"
*/



再看最简的版本,竟然简化到极致了,以至于无法再添加新需求。



纵向扩展这个,则是从一元二维,增加到一元四维,感觉是嵌套的,就像是增加了思维深度似的,一下让思维负担变重了。这可能和我们日常最多用到三维有关,再往高维就不熟悉了。



所以,第一个选择倾向出来了:横向扩展要优于纵向扩展。



再对比看下两个版本,简化版的优势体现在哪里?直观的观察就是使用的字符少了很多,细节版的繁琐体现在使用了临时变量,但这里的临时变量并没有带来更多的价值。



现在第二个现在倾向也出现了:减少低价值的重复表达,尽量精简化。



那,结果已经很明显了,需要一种横向扩展的,又能简化掉临时变量的表达。



今天的主角登场了,使用 pipe 这个高阶函数来同时满足这两个要求。



import pipe from 'lodash/fp/flow';
const doStuffBetter = pipe(
g,
trace('after g'),
f,
trace('after f')
);



一般介绍高阶函数的教程都只讲结果和用法,今天的日课从理解的容易和表达的简化两个维度分析了该组合方式的优势,希望能帮你加深理解。





发布于: 2020 年 07 月 04 日阅读数: 80
用户头像

顿晓

关注

因观黑白愕然悟,顿晓三百六十路。 2017.10.17 加入

视频号「编程日课」 一个不爱编程的程序员, 一个用软件来解决问题的工程师, 一个有匠心的手艺人。

评论

发布
暂无评论
编程核心能力之组合