- 论坛徽章:
- 0
|
对于coroutine,C也是有对策滴。C创造了lua,怎么不会lua的技能呢。
实现coroutine的库
讲解corout ...
mirnshi 发表于 2011-10-27 10:07
其实,标准的C库是这样玩的:
HANDLE initRC4(...) //初始化RC4算法,返回的handle关联于一个结构体
char RC4_iterator(HANDLE h)
{
unsigned k, byte;
//do sth with RC4(h)->i and RC4(h)->j and etc..
return byte
}
一样和算法描述文档一一对应……
当然,coroutine的确还是方便很多的,毕竟不需要像上面一样手工维护一个控制块了。不过这并不说明它就提供了什么没它就没法实现的功能……
像函数式语言在并行上的优势这类东西,才是其它语言难以取代的——注意这是在机器自动优化方面的优势;传统语言一样可以写map-reduce。
换言之,函数式语言可以不管你需不需要,只要机器有能力,就并行执行所有可以并行的代码;而传统语言必须自己分析哪些代码可以并行、并且还得写代码探测当前机器可以提供什么样的并行支持。
这里面很关键的一点是:任何语言的使用者,在关注问题本身之外,都必须关注如何把问题用目标语言表述出来。
在RC4的例子里,c之类语言的使用者在考虑如何表述时,需要额外考虑状态保持问题;而有coroutine的语言则不必考虑这种问题。
但这并不是不需代价的。因为c语言的使用者只需了解指针和它的32个关键字、48种运算符;有coroutine的语言使用者则还需要了解额外的yield关键字。
(嗯,或许上面这段话还要加上一个无聊的前缀:除了机器本身之外……)
当然,仅仅一个yield关键字没什么。但关键字更多一些呢?再多一些?
尤其要注意的是,这些关键字并不是独立的。它们必须能够和其它任何关键字、数据类型协同动作,才可能构成可靠的程序。
比如说,就好像这几天讨论的:c++为何要搞个引用呢?因为它增加了类和运算符重载概念!
而且,有了类,new/delete就是必要的;可是,还有传统数组?new[]和delete[]赶紧来帮个忙!
再如,有了const关键字,把它应用到自定义类对象上如何?不好意思……没法支持,除非你把const写到参数之前、成员函数声明之后等等几乎所有能想到的地方……而且写在每个地方的精确意义,还各不相同!
显然,一个新特性、一个新关键字的加入,带给一个语言的复杂度,可能是指数级的。
事实上,早在图灵之前,计算机已经有了——不仅有最古老的算盘,还有近代科技的结晶:精密的机械计算器,甚至还有电子管组成的庞然大物。
可以说,任何被人们充分认识的可靠规律,都可以利用来实现某种计算;而且只要肯动脑筋,哪怕是一两百年前对规律的认识,都可能足以支持任何计算。
比如说,为了计算特定的某种积分,可以选择特定参数设计合适电路,在输出端挂个仪表测量之;然后通过其它装置,将仪表读数查表转换成结果:哪种现代计算机可以做的如此简洁?利用现代元器件实现这种东西,哪种现代计算机能达到同样的效率?
但它们都成不了气候。原因就是:支持通用计算的机器,设计复杂度失控。
所以,图灵之前,只有专用计算器,没有通用计算机。
图灵的贡献,或者说图灵机的优点,不是复杂、晦涩;而是简单。
从此,不再需要什么积分电路、不再需要考虑积分、矩阵等等都该如何支持,只要支持布尔代数即可。
编程语言方面,其实完全一样: 仅仅布尔代数已经足以实现所有功能;但如果加上额外的一些支持,人们做事就会更加容易。
于是,从布尔代数的与或非三种逻辑、真假两种取值,发展到了存储程序、又到了C的32个关键字、48种运算符;然后又发展出面向对象、泛型等等新奇玩意儿。
不过,虽然新添加的这些东西,每一样看起来都带来了不少方便;但它们加起来之后,可能就是复杂度失控。
重复一次前面的论断: 伴随着每一个关键字、每一个新特性的加入,都可能导致语言复杂度指数级增长。
或许,32个关键字,普通人不到半年就可以掌握;64个关键字,可能也不过是10年苦读而已;而128甚至256个关键字,可能就只能寄希望一个长生不老的变态学到宇宙终结了。
反过来说,使用布尔代数编程,虽然没什么不能实现的,但需要考虑的细节太多太多,稍大的程序就会超出人们的能力范围;这是简化的极端。
使用c编程,可以做的东西和布尔代数一样多,但需要考虑的东西大幅减少,人们这才有能力写必须数百上千甚至几千行代码才能解决的复杂问题;
而使用c++的全部特性编程,可以做的东西还是和布尔代数一样多;但人们不得不拿出绝大部分的精力去应付它本身的边边角角,以至于除非限制使用它的一部分特性,否则就不可能写出很大的程序——这当然还不是复杂的极限。
——注意,把程序模块化可以把需要解决的问题分解、独立出来。这些模块综合起来可能超过千万甚至亿万行代码。
——哪怕是布尔代数,在模拟函数机制、然后把问题模块化后,同样可以支持千万行代码。这是模块分解的功劳,和语言无关。
——所以,上面说c可以写几百几千行的复杂程序,并不是笔误。确切含义是“一个不可分解的复杂问题,在其规模为几百甚至几千行时,仍然可以被C支持”:当然,这种问题可能并不存在,这里只是为了把模块分解的影响独立出去的一个思想实验罢了。
c显然并不刚好使得“语言本身的复杂度-工程复杂度”函数取最小值;但可以肯定它极其接近(稍偏复杂);而c++显然比c更偏复杂一些。python可能比它们两个都更靠近函数曲线的最小值。
lisp等函数系语言,有不同于布尔代数的另一个起点。从这个起点开始设计的编程语言,关联着另外一个“语言本身的复杂度-工程复杂度”函数。这个函数曲线的最小值可能会比基于图灵机的语言更低,也可能更高,目前尚无法判定。
当然,具体到某个类型的工程上,“语言本身的复杂度-工程复杂度”函数可能非常不同。这使得人们必须根据实际需要,选择更合适的语族。
但有一点是不变的(也是违背很多人的“常识”的):更复杂的语言可以简化更小的工程,但随着复杂度的增加,它甚至可能无法支持并不特别复杂的工程。
典型的例子就是c++的模板: 用它来实现某种类型无关的算法,的确比管理一大堆只有参数不同的函数省力;但在它实现每一个算法时,难度都远大于实现针对特定类型的算法。
这使得它只适合于写重用率极高、但目标较为单一的库。即便如此,这些库,如stl,仍然是如此复杂,以至于对绝大多数的人来说如同天书——相信即便是它们的作者,排错时也少不得要骂娘。
可是,stl 所实现的,只是平常到不能再平常的一组基本算法而已。
可以肯定,c/python都不是最好的语言。真正取得“语言本身的复杂度-工程复杂度”函数曲线最小值的语言,可能需要基础理论的突破才有可能,这也正是函数式语言引起关注的根本原因之一;
而c++引入的类、模板等等,至少在这么多年的实践之后,并不能证明它就是增加软件生产力的正确方向。
当然,模板在写库方面,取得的成就是有目共睹的。但这不代表这个方向就是正确的。
————————————————————————————————————————
如果说,结构化编程的成就是“强制划分函数,区分函数和数据结构”的话,面向对象的成就就是“强制划分模块,区分接口和实现”。
而函数和模块这两个概念,才真正推动了软件工程的发展,简化了编程难度;也只有和函数/模块直接相关的概念,才值得冒着指数增加语言复杂度的风险添加支持。
现在的乱象,其实是“模块本身被语言支持之后”引起的连锁反应。
或者说,因为模块相关概念的加入,各种语言不得不提供种种便利;这就启动了“关于模块概念的复杂度指数增长过程”。
而这新添加的一堆堆所谓的便利,大部分是实质的负担;换句话说,那些新语言、新特性,解决的问题往往远比它带来的更多。
最终,哪些东西更基本、更不可替代,它们才会被保留下来;甚至可能出现真正的、深刻揭示本质的理论,完成类似图灵机的提炼。
也只有经过这样的简化和提炼,面向对象(或者,面向接口)才可能保证提供的便利远大于麻烦,才有资格取代结构化编程,才有资格声称自己开创了一个新时代。 |
|