控制结构(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 ...
随机推荐
- 【NO.3-2】Jmeter - 在Linux配置HOSTS的方法
在Linux配置HOSTS 咱平时在Windows系统做web测试的时候需要修改HOSTS文件.Linux也一样. /*在Linux执行性能测试的时候,很容易忘记配置HOSTS,那么你发送的请求的响应 ...
- Vue组件实例间的直接访问
前面的话 有时候需要父组件访问子组件,子组件访问父组件,或者是子组件访问根组件. 在组件实例中,Vue提供了相应的属性,包括$parent.$children.$refs和$root,这些属性都挂载在 ...
- NSThread创建线程的三种方法
- (IBAction)NSThreadBtnClick:(id)sender { [self threadObjectMethod]; [self threadClassMethod]; [self ...
- html table中单元格自动换行
table中单元格自动换行样式: table-layout: fixed; word-wrap: break-word; table-layout 可能的值(IE不支持inherit属性) 值 描 ...
- js 正则,从url中取参数值
function getQueryString(name) { var reg = new RegExp(name +"=([^&]*)"); var r = window ...
- Writing A Threadpool in Rust
文 Akisann@CNblogs / zhaihj@Github 本篇文章同时发布在Github上:https://zhaihj.github.io/writing-a-threadpool-in- ...
- 30岁后还能入IT行业吗?
前言 写这样的文章只想给那些准备改行想入IT行业的同学一点点建议,当然只做参考,我的个人经历. 我也是一个30岁改行进IT行业的程序猿.入这行也有一年多了吧.给我的感觉就是比较苦逼. 入行前的培训 进 ...
- [读书笔记] 二、条件注解@Conditional,组合注解,元注解
一.条件注解@Conditional,组合注解,元注解 1. @Conditional:满足特定条件创建一个Bean,SpringBoot就是利用这个特性进行自动配置的. 例子: 首先,两个Condi ...
- Spark Structured streaming框架(1)之基本使用
Spark Struntured Streaming是Spark 2.1.0版本后新增加的流计算引擎,本博将通过几篇博文详细介绍这个框架.这篇是介绍Spark Structured Streamin ...
- 通过VM虚拟机安装Ubuntu server部署flask项目
1. VM安装Ubuntu server 14.04,系统安装完成后,首先安装pip工具方便之后的包安装,此处需先使用 apt-get install update,apt-get install u ...