// 上一篇:管道(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)的更多相关文章

  1. 控制结构(8) 线性化(linearization)

    // 上一篇:管道(pipeline) // 下一篇:程序计数器(PC) "编程语言不过是一个工具,什么语言都一样","编程语言能改变人的思维,不同的语言会带给你不同的思 ...

  2. 控制结构(7) 程序计数器(PC)

    // 上一篇:最近最少使用(LRU) // 下一篇:线性化(linearization) 程序的每一行都是一个状态,对应的行指令.同步的情况下同一个pc一直自增,异步的时候,分裂出一个新的子pc,独立 ...

  3. Scala入门3(特质线性化)

    尝试设计一套特质,灵活的改动整数队列.队列有两种操作:put把整数放入队列,get从尾部取出它们.队列是先进先出的,get应该依照入队列的顺序取数据.提示:可以用mutable.ArrayBuffer ...

  4. feilong's blog | 目录

    每次把新博客的链接分享到技术群里,我常常会附带一句:蚂蚁搬家.事实上也确实如此,坚持1篇1篇的把自己做过.思考过.阅读过.使用过的技术和教育相关的知识.方法.随笔.索引记录下来,并持续去改进它们,希望 ...

  5. Python’s super() considered super!

    如果你没有被Python的super()惊愕过,那么要么是你不了解它的威力,要么就是你不知道如何高效地使用它. 有许多介绍super()的文章,这一篇与其它文章的不同之处在于: 提供了实例 阐述了它的 ...

  6. 控制结构(9) 管道(pipeline)

    // 上一篇:线性化(linearization) // 下一篇:指令序列(opcode) 最近阅读了酷壳上的一篇深度好文:LINUX PID 1 和 SYSTEMD.这篇文章介绍了systemd干掉 ...

  7. 控制结构(9): 管道(pipeline)

    // 上一篇:线性化(linearization) // 下一篇:指令序列(opcode) 最近阅读了酷壳上的一篇深度好文:LINUX PID 1 和 SYSTEMD.这篇文章介绍了systemd干掉 ...

  8. 控制结构(2) 卫语句(guard clause)

    // 上一篇:分枝/叶子(branch/leaf) // 下一篇:状态机(state machine) 基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构. 典型代码: 同步版本 f ...

  9. 控制结构(2): 卫语句(guard clause)

    // 上一篇:分枝/叶子(branch/leaf) // 下一篇:状态机(state machine) 基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构. 典型代码: 同步版本 f ...

随机推荐

  1. webpack4.0各个击破(1)—— html部分

    webpack作为前端最火的构建工具,是前端自动化工具链最重要的部分,使用门槛较高.本系列是笔者自己的学习记录,比较基础,希望通过问题 + 解决方式的模式,以前端构建中遇到的具体需求为出发点,学习we ...

  2. vs2017安装pygame,vs2017安装python第三方包

    vs2017有独立的python环境:所以想在vs2017开发python并使用第三方包,需要在vs2017中操作,完成第三方包的安装. 一,查看vs2017有哪些版本的python,当前使用的是哪个 ...

  3. PHP中利用pcntl实现多进程(模拟多线程)实例(转)

    windows不支持pcntl的多线程(非Unix类系统不支持此模块),pcntl在很久很久之前就听过了,但是一直没有尝试着真正要用它. 这不,遇到socket问题了,看socket,遇到pcntl了 ...

  4. MySQL Err(1024):Lock wait timeout exceeded; try restarting transaction

    查看事务是否占用被锁: SELECT * FROM information_schema.INNODB_TRX;查看里面的 trx_mysql_thread_id字段 show full proces ...

  5. vue2.5.2版本 :MAC设置应用在127.0.0.1:80端口访问; 并将127.0.0.1指向www.yours.com ;问题“ Invalid Host header”

    0.设置自己的host文件,将127.0.0.1指向自己想要访问的域名 127.0.0.1 www.yours.com 1.MAC设置应用在127.0.0.1:80端口访问: config/index ...

  6. jQuery ajax-param()

    param() 方法创建数组或对象的序列化表示. 该序列化值可在进行 AJAX 请求时在 URL 查询字符串中使用. 序列化一个 key/value 对象: var params = { width: ...

  7. Echarts地图使用经验-地图变形和添加数据

    关于echart2,echart3地图的使用一点人生经验: 1.echart3,echart2加载地图变形修复. 最近在使用echart2使用过程中,发现加载海南地图会产生变形.如下图,海南地图产生了 ...

  8. 浅谈OA系统与Portal门户的区别

    随着社会信息化的发展与进步,OA办公自动化软件打破了传统复杂的办公方式,使各个行业实现了高效的无纸化办公.由此一来OA快速成长为继财务软件.ERP软件之后的第三大管理软件.随着企业信息化系统的不断增多 ...

  9. spring学习总结——装配Bean学习三(xml装配bean)

    通过XML装配bean Spring现在有了强大的自动化配置和基于Java的配置,XML不应该再是你的第一选择了.不过,鉴于已经存在那么多基于XML的Spring配置,所以理解如何在Spring中使用 ...

  10. php操作Memcache的一个类库

    ###php操作Memcache的一个类库 代码如下: <?php /** * Created by PhpStorm. * User: alisleepy * Date: 2019-03-14 ...