控制结构(8): 线性化(linearization)
// 上一篇:管道(pipeline)
// 下一篇:程序计数器(PC)
“编程语言不过是一个工具,什么语言都一样”,“编程语言能改变人的思维,不同的语言会带给你不同的思考方式”,这是我学习编程之后经常听到的两种说法。到底哪一种更靠谱呢?这要看说者从什么角度去考虑的。
同样是“编程语言不过是一个工具,什么语言都一样”这句话,不同的人所指的也不一样。有的人用什么语言都只写基本都CRUD代码,那他可能会觉的好像换个语言也没什么差别,所以会觉的用什么语言都一样。有的人并不怎么关心技术内功,只是完成工作,早早有机会就转去做非技术工作,他可能也会觉的编程语言不过是一个工具,对他来说没什么差别。有的人则是用过各种语言,做过各种项目,深刻理解到了不同语言共性的地方,例如在解决很多领域问题的时候,语言的实现上是一回事,但怎样对领域特定问题建模,构架可能是更重要的,实现上的差异此时可能不起决定作用,他可能会说编程语言是个工具。诸如此类,不一而足。
而持“编程语言能改变人的思维,不同的语言会带给你不同的思考方式”的人,也大致有分类。有的人并没有深入理解日常使用的一个编程语言,频繁辗转于学习各种新新语言,每个语言都写写HelloWord,可能觉的不同的语言其实没什么差别,或者好像某个语言只是多了一些不同的关键字,少了一些语言特性等等。这样的时候,会迷失在不同语言的表象之下。而有的人深入理解了日常使用的语言,并用它开发了实际有用的软件、项目、产品等,包括业务系统和核心系统,完整的工程实践让他理解到了很多语言特性的优劣,这些优劣不仅仅局限在“哇,这个语言太牛了,如此的自由表达”这样的“做加法”的理解上,而是包括了“嗯,这个语言的这几个设计让程序开发在整体上倾向于模块化、可维护、避免复杂细节”,以及“嗯,这个语言的这些特性要限制性使用,它们会带来一些状态爆炸以及难以维护的意大利面条”,“这个语言在错误处理上提供了不错的机制,这个机制可以更快发现问题而不是隐藏问题”,等等这样的思考。这样的经过工程实践的理解是另外一种。也有人说不同的编程语言提供了不同的范式:“面向过程、面向对象、函数式”等等,会写function不等于面向过程就学好了,会写class也不等于面向对象就学好了,会写lambda不代表对函数式里的无状态假设就理解到位了。
比如“全局变量是代码不可维护的根源”这点,在不同语言里可能有不同形式的存在:
- 直接使用全局变量+多个全局函数,这些全局函数直接访问全局变量。类似这样:
int g_xxx;
float g_yyy;
void do_something1(){
g_xxx = ...;
}
void do_something2(){
g_yyy = gxxx + ...;
}
...
也许一开始代码很少,可以这样写,但是当项目演化、需求变更,全局变量就各种跳线,会形成意大利面条代码,难以维护。
- 有人说,那我全部装进一个class总可以了吧?此时要避免“万能类”,类似这样:
class Manager{
int g_xxx;
float g_yyy;
void do_something1(){
g_xxx = ...;
}
void do_something2(){
g_yyy = gxxx + ...;
}
//...
void do_something100(){
g_yyy = gxxx + ...;
}
}
在一个class里面塞进了太多的功能,也不分模块,一样是犯了“全局变量”的毛病。即使是有经验的程序员,在面对项目演化,需求变更,BUG修改的时候,也可能会把本来状态简单的一个类改着改着变成一个万能类。因此,在这个过程中要写出真正的只干该干的事情的Object,而不是挂羊头卖狗肉的函数和数据的集合。
- 换一个带有闭包的语言,闭包带来了便利,使得我们不必写非常多的辅助函数和辅助类就可以在一个闭包里完成一堆功能。类似这样:
function dosomething(onDone)
local conext = {
state = ..,
data = ...
}
local function do_something1(onComplete)
context.data = ...
end
local function do_something2(onComplete)
context.state = ...
end
local function do_something2_rec(onComplete)
....
do_something1(function()
do_something2_rec(function()
....
end)
end)
end
.....
do_something2_rec(function()
onDone()
end)
end
在这里,一个闭包里做了递归之后,实际上这些闭包函数+context,构成了一组本来应该一个class做的事情,这样的代码如果不是逻辑清晰和直观的,久而久之就会变的难以维护。一个闭包隐约等价于一个class等构造函数,如果说在class的构造里,我们要注意避免资源的循环引用,那么在闭包里同样是需要的。例如:
function connect()
local connection = createConnection()
connection:OnStateChange(function(conn,oldState,newState)
connection.xxx = ...
end)
end
这里就在OnStateChange里闭包里connection对象自身,只要这个闭包还存活着,connection就不会死掉。所以需要小心的在必要的地方移除注册的闭包。另外一种就是在OnStateChange里不去闭包外部的connection,而是使用函数本身的参数conn。
实际上,当闭包变的复杂,我们可以通过将其重新封装成一个对象来让代码模块化和可维护:
function dosomething(onDone)
let obj = Context:New(...)
obj.run(function()
onDone()
end)
end
重新来看“全局变量”,应该是这样的角度去理解:
全局变量+多个全局函数,等价于在函数内的闭包变量+多个嵌套函数,等价于在写在一个类内部的多个应该被拆分的逻辑子类,都是耦合和不可维护的根源。
而如果从函数式语言的角度来看,函数式语言里提供lambda反而没有这么多毛病,因为函数式语言是无状态的,所以闭包并不会因为状态的组合导致副作用。
从而,我们可以回过头理解下关于函数的“自由变量”:
function test(a,b)
return a+b+d;
end
a和b都是test函数的约束变量(bound variable),而d是test函数的自由变量(free variable)。无论这个函数是在全局的,calss里的、局部的。自由变量导致函数不可重入,有副作用,所以在非函数式语言里写局部函数、匿名函数都应该注意减少自由变量,减少函数的副作用。从这个角度来说,一个闭包可以看成是功能比较强的函数,同时也是一个功能比较弱(模块化,封装性差)的class。
控制结构(8): 线性化(linearization)的更多相关文章
- 控制结构(8) 线性化(linearization)
// 上一篇:管道(pipeline) // 下一篇:程序计数器(PC) "编程语言不过是一个工具,什么语言都一样","编程语言能改变人的思维,不同的语言会带给你不同的思 ...
- 控制结构(7) 程序计数器(PC)
// 上一篇:最近最少使用(LRU) // 下一篇:线性化(linearization) 程序的每一行都是一个状态,对应的行指令.同步的情况下同一个pc一直自增,异步的时候,分裂出一个新的子pc,独立 ...
- Scala入门3(特质线性化)
尝试设计一套特质,灵活的改动整数队列.队列有两种操作:put把整数放入队列,get从尾部取出它们.队列是先进先出的,get应该依照入队列的顺序取数据.提示:可以用mutable.ArrayBuffer ...
- feilong's blog | 目录
每次把新博客的链接分享到技术群里,我常常会附带一句:蚂蚁搬家.事实上也确实如此,坚持1篇1篇的把自己做过.思考过.阅读过.使用过的技术和教育相关的知识.方法.随笔.索引记录下来,并持续去改进它们,希望 ...
- Python’s super() considered super!
如果你没有被Python的super()惊愕过,那么要么是你不了解它的威力,要么就是你不知道如何高效地使用它. 有许多介绍super()的文章,这一篇与其它文章的不同之处在于: 提供了实例 阐述了它的 ...
- 控制结构(9) 管道(pipeline)
// 上一篇:线性化(linearization) // 下一篇:指令序列(opcode) 最近阅读了酷壳上的一篇深度好文:LINUX PID 1 和 SYSTEMD.这篇文章介绍了systemd干掉 ...
- 控制结构(9): 管道(pipeline)
// 上一篇:线性化(linearization) // 下一篇:指令序列(opcode) 最近阅读了酷壳上的一篇深度好文:LINUX PID 1 和 SYSTEMD.这篇文章介绍了systemd干掉 ...
- 控制结构(2) 卫语句(guard clause)
// 上一篇:分枝/叶子(branch/leaf) // 下一篇:状态机(state machine) 基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构. 典型代码: 同步版本 f ...
- 控制结构(2): 卫语句(guard clause)
// 上一篇:分枝/叶子(branch/leaf) // 下一篇:状态机(state machine) 基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构. 典型代码: 同步版本 f ...
随机推荐
- 第35章 秘密(secrets) - Identity Server 4 中文文档(v1.0.0)
在某些情况下,客户端需要使用身份服务器进行身份验证,例如 在令牌端点请求令牌的机密应用程序(也称为客户端) API在内省端点验证引用令牌 为此,您可以将秘密列表分配给客户端或API资源. 秘密解析和验 ...
- C# 设置Excel条件格式(二)
上一篇文章中介绍了关于设置Excel条件格式,包括基于单元格值.自定义公式等应用条件格式.应用数据条条件类型格式.删除条件格式等内容.在本篇文章中将继续介绍C# 设置条件格式的方法. 要点概述: 1. ...
- Java开发笔记(五十五)关键字static的用法
前面介绍嵌套类的时候讲到了关键字static,用static修饰类,该类就变成了嵌套类.从嵌套类的用法可知,其它地方访问嵌套类之时,无需动态创建外层类的实例,直接创建嵌套类的实例就行.其实static ...
- Python全栈开发之---装饰器
1.装饰器的形成过程 import time def func1(): print('in func1') def timer(func): def inner(): start = time.tim ...
- zepto和jQuery on事件委托在苹果手机上的”坑“
移动端用zepto做的页面,突然发现on绑定的click事件并没有触发,代码如下: <ul> <li class="a">1111111aaaaaaasss ...
- AndroisStudio列选择模式
今天敲代码的时候可能由于误开了“列选择模式”,在移动光标时,发现若光标所在列超过当前行的行尾列,不像一般情况应该跳到行尾,而变成了保持列的位置,停在了超过行尾的空白处, 如图:非一般情况 一般情况: ...
- JHipster生成微服务架构的应用栈(一)- 准备工作
本系列文章演示如何用JHipster生成一个微服务架构风格的应用栈. 环境需求:安装好JHipster开发环境的CentOS 7.4(参考这里) 应用栈名称:appstack 认证微服务: uaa 业 ...
- Dubbo开发,利用项目模拟提供者和消费者之间的调用--初学
开发工具:IDEA,虚拟机 VMware Workstation 预备工作:安装好zookeeper的虚拟机,电脑jdk更换为1.7,本地tomcat启动,能够访问以下页面即可进行开发 2.建立以下s ...
- echarts中legend如何换行
lengend data数据中若存在'',则表示换行,用''切割.
- js学习之路3: 数据类型
1. 字符串: <!DOCTYPE html> <html> <body> <script> var apple = "苹果"; v ...