【冒号课堂——编程范式与OOP思想】上篇 读书笔记

今天读了郑晖老师的《冒号课堂》上篇,对编程范式有了更多的理解,有趣、有料、更有道,大呼过瘾!


越是喧嚣的世界,越需要宁静的思考,让躁动的心灵得以平息,让蕴藏的灵性得以释放。

“程序员是吃青春饭的职业”出自那些缺乏灵感和激情的人之口。

编程之时,你便进入自己创造的世界之中。这是你的世界,只有注入你的想象力、创造力和激情,它才有勃勃生机。你编写的岂止是代码,分明还有乐曲;你敲击的岂止是键盘,分明还有琴键;你运行的岂止是程序,分明还有世界。当优美的旋律奏起,整个世界都随之翩然起舞,一种莫可名状的满足是否会充溢你的全身? 找对象是“对象导向”的,去约会是“面向对象”的。

掌握一门语言的语法、工具和技巧固然重要,但那只相当于学会一门兵器的招法,更重要的当然是心法。招法重形,心法重意。得形而忘意,无异舍本逐末;得意而忘形,方能游刃有余。

所谓编程范式(programming paradigm),指的是计算机编程的基本风格或典范模式。借用哲学的术语,如果说每个编程者都在创造虚拟世界,那么编程范式就是他们置身其中自觉不自觉采用的世界观和方法论。”

编程范式是计算机编程中的基本风格和典范模式,是编程者在其所创造的虚拟世界中自觉不自觉采用的世界观和方法论。每种范式都引导人们带着其特有的倾向和思路去分析和解决问题。OOP就是一种编程范式。

如果把一门编程语言比作兵器,它的语法、工具和技巧等是招法,它采用的编程范式则是心法。

库和工具包是为程序员带来自由的,框架是为程序员带来约束的。具体地说,库和工具包是为程序员提供武器装备的,框架则利用控制反转(IoC)[6]机制实现对各模块的统一调度,从而剥夺了程序员对全局的掌控权,使他们成为手执编程武器、随时听候调遣的士兵。”

与前面说的框架与库和工具包不同,设计模式(design pattern)和架构(architecture)不是软件产品,而是软件思想。设计模式是软件的战术思想,架构是软件的战略决策。

至于架构,一般指一个软件系统的最高层次的整体结构和规划,一个架构可能包含多个框架,而一个框架可能包含多个设计模式。”

语感是一个人对语言的敏锐感知力,反映了他在语言方面的整体上的直觉把握能力。语感强者,能听弦外之音,能说双关之语,能读隽永之作,能写晓畅之文。这是一种综合的素质和修养,其重要性是不言而喻的。那么如何培养语感呢?普通的学习和训练固不可少,但如果忽视语言背后的文化背景和思维方式,终究只是缘木求鱼。编程范式正体现了编程的思维方式,因而是培养编程语言的语感的关键。

库和工具包侧重代码重用,框架侧重设计重用。库和工具包从微观上解决具体问题,是为程序员带来自由的;框架从宏观上控制软件整体的结构和流程,是为程序员带来约束的。框架是通过控制反转(IoC)机制反客为主的。

设计模式是软件的战术思想,架构是软件的战略决策。与框架、库和工具包不同,它们不是软件产品,而是软件思想。

设计模式与惯用法都是针对常发问题的解决方案,但前者偏重设计,后者偏重实现。

架构太高,谈之过早;框架太多,无从谈起;设计模式太少,不必多谈。至于编程范式,对培养编程语言的语感至关重要,需要充分的重视和长期的积累,方能悟其精髓。

命令式编程是电脑——准确地讲,是冯·诺伊曼机(von Neumann machine)——运行机制的抽象,即依序从内存中获取指令和数据,然后去执行。从范式的角度看,其世界观是:程序是由若干行动指令组成的有序列表。其方法论是:用变量来存储数据,用语句来执行指令。”

“从理论上而言,完全可以有非命令式的机器语言存在,前提是计算机采用了特殊的硬件实现,比如非冯·诺伊曼结构的数据流机(dataflow machine)和归约机(reduction machine)。但这类计算机并未流行于市,相应的机器语言自然罕见了[

“根据结构化定理(structured program theorem)[2],任何程序都可用顺序(concatenation)、选择(selection)和循环(repetition)等3种基本控制结构来表示。”

结构化编程的思想包括两方面。在微观上,主张循规守矩,采用顺序、选择和循环3种逻辑结构,摒弃或限制goto语句[4],以避免杂乱无章的代码。在宏观上,主张分而治之(divide and conquer),采用‘自顶向下(top-down)’[5]的设计,通过模块化将一个较为复杂的系统分解为若干相对简单的子系统,每个子系统可以独立地进一步分解,直到容易编码实现为止。这两方面是互为因果、互为保障的——由基本结构拼装而成的系统一定是模块清晰、层次分明的;反之,系统逐步分解到最后,一定会演化成基本结构。”

声明式编程由若干规范(specification)的声明组成的,即一系列陈述句:‘已知这,求解那’,强调‘做什么’而非‘怎么做’。声明式编程是人脑思维方式的抽象,即利用数理逻辑或既定规范对已知条件进行推理或运算。”

“声明式编程发轫于人工智能的研究,主要包括函数式编程(functional programming,简称FP)和逻辑式编程(logic programming,简称LP)。其中,函数式编程将计算描述为数学函数的求值,而逻辑式编程通过提供一系列事实和规则来推导或论证结论。

起源的不同决定了这两大类范式代表着迥然不同的编程理念和风格:命令式编程是行动导向(Action-Oriented)的,因而算法是显性而目标是隐性的;声明式编程是目标驱动(Goal-Driven)的,因而目标是显性而算法是隐性的。

编程语言的流行程度与其擅长的领域关系密切。声明式语言——尤其是函数式语言和逻辑式语言——擅长基于数理逻辑的应用,如人工智能、符号处理、数据库、编译器等,对基于业务逻辑的、尤其是交互式或事件驱动型的应用就不那么得心应手了。而大多数软件是面向用户的,交互性强、多为事件驱动、业务逻辑千差万别,显然命令式语言在此更有用武之地。”

一则迭代比递归更符合命令式的思维模式,因为前者贴近机器语言而后者贴近数学语言;二则除尾递归(tail recursion)[10]外,一般递归比迭代的开销(overhead)大。

归根结底,编程是寻求一种机制,将指定的输入转化为指定的输出。3种范式对此提供了截然不同的解决方案:命令式把程序看作一个自动机,输入是初始状态,输出是最终状态,编程就是设计一系列指令,通过自动机执行以完成状态转变;函数式把程序看作一个数学函数,输入是自变量,输出是因变量,编程就是设计一系列函数,通过表达式变换以完成计算;逻辑式把程序看作一个逻辑证明,输入是题设,输出是结论,编程就是设计一系列命题,通过逻辑推理以完成证明。绘成

命令式编程通过一系列改变程序状态的指令来完成计算,声明式编程只描述程序应该完成的任务。命令式编程模拟电脑运算,是行动导向的,关键在于定义解法,即“怎么做”,因而算法是显性而目标是隐性的;声明式编程模拟人脑思维,是目标驱动的,关键在于描述问题,即“做什么”,因而目标是显性而算法是隐性的。

函数式编程通过数学函数的表达式变换和计算来求值。

逻辑式编程通过一系列事实和规则,利用数理逻辑来推导或论证结论。

命令式编程中的变量代表抽象化的内存,所存内容可能改变。声明式编程中的变量代表抽象化的符号,所指对象一般不会改变。

声明式编程专注问题的分析和表达而不是算法实现,不用指明执行顺序,一般没有或极少有副作用,也不存在内存管理问题。这些都大大降低了编程的复杂度,同时也非常适合于并发式计算。

编程语言的流行程度与其擅长的领域密切相关。函数式语言和逻辑式语言擅长基于数理逻辑的应用,命令式语言擅长基于业务逻辑的、尤其是交互式或事件驱动型的应用。

声明式语言与命令式语言之间并无绝对的界限,它们均建立于低级语言之上,并且互相渗透融合。

在命令式语言中引入函数或过程,是一种向声明式风格的趋近。

种核心编程范式采用如下不同的机制—— 命令式:自动机机制,通过设计指令完成从初始态到最终态的转变。 函数式:数学变换机制,通过设计函数完成从自变量到因变量的计算。 逻辑式:逻辑证明机制,通过逻辑推理完成从题设到结论的证明。

如果把整个流程看作一颗倒长的大树,过程式编程自树根向下,逐渐分支,直到每片树叶,类似数学证明中的分析法,即执果索因的逆推法;OOP则从每片树叶开始,逐渐合并,直到树根,类似数学证明中的综合法,即执因索果的正推法。

过程式程序的世界是君主制的;OO程序的世界是民主制的。 封装使得公民拥有个体身份,继承使得公民拥有家庭身份,多态使得公民拥有社会身份。

OOP(Object-Oriented programming)是一种计算机编程模式,它将对象作为问题空间的基本元素,利用对象和对象间的相互作用来设计程序。所谓对象,是实际问题中实体的抽象,具有一定的属性和功能。OOP的3个基本特征是:封装性、继承性和多态性。所谓封装性就是——”

OOP虽然是在命令式的基础上发展起来的,但其核心思想可泛化为:以数据为中心组织逻辑,将系统视为相互作用的对象集合,并利用继承与多态来增强可维护性、可扩展性和可重用性。这种思想也能应用到函数式和逻辑式中,只不过对象的方法从命令式中的过程分别换成函数式中的函数和逻辑式中的断言罢了。大致说来,命令式、函数式和逻辑式互相平行,而OOP与它们正交。”

OOP最适用于大型复杂的、交互式的,尤其是与现实世界密切相关的系统,但在小型应用、数学计算、符号处理等方面并无优势。

首先,将可维护性、可扩展性和可重用性与OOP划等号,是只见树木,不见森林——那是所有范式和语言的共同目标。其次,以C语言为例,信息隐藏可用关键字static来实现;继承可用合成(composition)来代替;多态可以利用函数指针来实现。更何况这些只是手段而非目的,只要设计合理,C程序同样具有可维护性、可扩展性和可重用性,性能效率还更优越。即使在OOP日益风行的今天,C的占有率始终稳踞前列,许多大型复杂软件如操作系统、数据库等仍以C为主,这足以证明其仍堪大用。”

如果把整个流程看作一颗倒长的大树,过程式编程自树根向下,逐渐分支,直到每片树叶,类似数学证明中的分析法,即执果索因的逆推法;OOP则从每片树叶开始,逐渐合并,直到树根,类似数学证明中的综合法,即执因索果的正推法。”

OOP以对象为基本模块单位,而对象是现实中具体事物和抽象概念的模拟,这使得编程设计更自然更人性化。”

“对一个没有独立思考习惯的人来讲,与其说他认同一个理论,倒不如说他认同该理论倡导者的权威。而在他仰视权威的同时,也把自己的思想交托给了权威。”

过程式编程的模块以函数为单位,OOP的模块以对象为单位,二者的区别是:函数是被动的实体,对象是主动的实体。过程式程序的世界是君主制的,主函数是国王,其他函数是臣民,等级分明,所有臣民在听命于上级的同时也对下级发号施令,最终为国王服务;OO程序的世界是民主制的,所有对象都是独立而平等的公民,有权利保护自己的财产和隐私并向他人寻求服务,同时有义务为他人提供承诺的服务,公民之间通过信息交流来协作完成各种任务。更进一步地,封装使得公民拥有个体身份,须要对自己负责;继承使得公民拥有家庭身份,须要对家庭负责;多态使得公民拥有社会身份,须要对社会负责。

“并发式编程以资源共享与竞争为主线——又是对当今世界形势的一个逼真模拟。这意味着程序设计将围绕进程的划分与调度、进程之间的通信与同步[18]等来展开。合理的并发式设计需要诸多方面的权衡考量。”

过程式:以过程为模块的君主体系,模块之间互相授命与听命。 函数式:以函数为模块的数学体系,模块之间互相替换与合成。 逻辑式:以断言为模块的逻辑体系,模块之间互相归纳与演绎。 对象式:以对象为模块的民主体系,模块之间互相交流与服务。 并发式:以进程为模块的生产体系,模块之间互相竞争与合作。

并发式编程以进程为导向,以任务为中心,以资源共享与竞争为主线。

并发式编程有助于提高运行效率、充分利用资源、提高软件的响应能力、改善用户体验、保证公平竞争,同时以进程为单位将系统模块化,更加真实地模拟现实世界。

尾递归是一种特殊的递归,其递归调用出现在函数的最后一步运算(尾部)。这类递归很容易通过手工或编译器转化为迭代形式,以优化性能。

“STL有3要素:算法(algorithm)、容器(container)和迭代器(iterator)。算法是一系列切实有效的步骤;容器是数据的集合,可理解为抽象的数组;迭代器是算法与容器之间的接口,可理解为抽象的指针或游标。”冒号讲述道,“算法串联数据,如脊贯肉;数据实化算法,如肉附脊。只有抽象出表面的数据,算法的脊梁才能显现。

泛型编程是算法导向(Algorithm-Oriented)的,即以算法为起点和中心点,逐渐将其所涉及的概念(如数据结构、类)内涵模糊化、外延扩大化,将其所涉及的运算(函数、方法、接口)抽象化、一般化,从而扩展算法的适用范围。这非常类似数学思维——当数学家证明完一个定理后,总会试图在保持核心思想的前提下,尽可能地放宽题设,增强结论,从而推广定理。外行人常以为数学定理最重要,其实数学思想才是数学的精髓。比如,举世皆知的哥德巴赫猜想和费尔马大定理,人们在攻克它们的过程中产生的新思想、新理论、新方法,已远远超过了定理本身的意义。数学家们甚至不愿这些猜想被过早地解决,怕扼杀了会下金蛋的鸡。在他们眼里,思想是鸡,结论是蛋。这也无怪乎STL会出自一位学数学的人之手了。[4]”

泛型编程能打破静态类型语言的数据类型之间的壁垒,在不牺牲效率并确保类型安全的情况下,最大限度地提高算法的普适性。

STL有3要素:算法、容器和和迭代器。算法是一系列可行的步骤;容器是数据的集合,是抽象化的数组;迭代器是算法与容器之间的接口,是抽象化的指针。算法串联数据,数据实化算法。

泛型编程不仅能泛化算法中涉及的概念(数据类型),还能泛化行为(函数、方法、运算)。

泛型编程是算法导向的,以算法为中心,逐渐将其所涉及的概念内涵模糊化、外延扩大化,并将其所涉及的运算抽象化、一般化,从而提高算法的可重用性。

模板元编程即Template Metaprogramming,与泛型编程密切相关但自成一派,隶属于另一种编程范式——元编程(Metaprogramming),简称MP。此处的前缀‘meta-’常译作‘元’,其实就是‘超级’、‘行而上’的意思。比如,元数据(Metadata)是关于数据的数据,元对象(Metaobject)是关于对象的对象,依此类推,元编程自然是关于程序的程序,或者说是编写、操纵程序的程序。”

在传统的编程中,运算是动态的,但程序本身是静态的;在元编程中,二者都是动态的。元程序将程序作为数据来对待,能自我发现、自我赋权和自我升级,有着其他程序所不具备的自觉性、自适应性和智能性,可以说是一种最高级的程序。它要求编程者超越常规的编程思维,在一种崭新的高度上理解编程。

元编程是编写、操纵程序的程序。在传统的编程中,运算是动态的,但程序本身是静态的;在元编程中,二者都是动态的。

➢ 元编程能减少手工编程,突破原语言的语法限制,提升语言的抽象级别与灵活性,从而提高程序员的生产效率。

元编程有诸多应用:许多开发工具、框架引擎之类的基础软件都有自动生成源代码的功能;创造DSL以便更高效地处理专门领域的业务;自动生成重复代码;动态改变程序的语句、函数,类,等等。 ➢ IDE下自动生成的代码通常局限性大且可读性差,小操作可能造成的源码上的大差异,削弱了版本控制的意义。用自编的无需人机交互的元程序来生成代码,只须将元程序的数据来源版本化,简明而直观。同时由于元程序可以随时修改,因此局限性小,更加灵活。 ➢ 语言导向式编程(LOP)通过创建一套专用语言DSL来编写程序。相比通用语言,DSL更简单、更抽象、更专业、更接近自然语言和声明式语言、开发效率更高,同时有助于专业程序员与业务分析员之间的合作。 ➢ 语言导向式编程一般通过元编程将专用语言转化为通用语言。 ➢ 产生式编程与静态元编程都能自动生成源代码。产生式编程强调代码的生成,元编程强调生成代码的可执行性。此外,动态元编程并不生成源代码,但能在运行期间修改程序。 ➢ 元程序将程序作为数据来对待,有着其他程序所不具备的自觉性、自适应性和智能性,可以说是一种最高级的程序。

SoC就是Separation of Concerns,即关注点分离;DRY是Don't Repeat Yourself,即尽量减少重复代码。”

不良代码通常有两种病征:一是结构混乱,或聚至纠缠打结、或散至七零八落;二是代码重复,叠床架屋、臃肿不堪。治疗此类病症一个有效的方法是抽象与分解:从问题中抽象出一些关注点,再以此为基础进行分解。分解后的子问题主题鲜明且独立完备,既不会牵一发而动全身,也不会四分五裂、支离破碎。同时具有相同特征的部分可以像代数中的公因子一样提取出来,提高了重用性,减少了重复性。”

在数学中互为正交的两个向量在彼此方向上投影为零,意味着彼此独立、互不影响,斜交可不行。”

SoC是Separation of concerns的缩写,指应将关注点分离;DRY是Don't Repeat Yourself的缩写,指应尽量减少重复代码。

抽象与分解是治愈代码紊乱、松散、重复的良方。

抽象与分解的原则是单一化和正交化,以保障软件系统符合“高内聚、低耦合”的要求。

横切关注点指与程序的纵向主流执行方向横向正交的关注焦点。

接入点是附加行为——建议(advice)的执行点,切入点(pointcut)是指定的接入点(join point)集合,这些接入点共享一段插入代码。切入点与建议组成了切面(aspect),是模块化的横切关注点。

编织是将附加的切面逻辑嵌入到主体应用程序之中的过程。编织分静态编织和动态编织两种。静态编织在编译期、后编译期或加载期嵌入代码,动态编织则在运行期嵌入。

AOP的实施分3步:切面分解、切面实现和切面合成。

OOP只能沿继承树的纵向方向重用,AOP可以沿横向方向重用。

何谓事件?通俗地说,它是已经发生的某种令人关注的事情。在软件中,它一般表现为一个程序的某些信息状态上的变化。基于事件驱动的系统一般提供两类的内建事件(built-in event):一类是底层事件(low-level event)或称原生事件(native event),在用户图形界面(GUI)系统中这类事件直接由鼠标、键盘等硬件设备触发;一类是语义事件(semantic event),一般代表用户的行为逻辑,是若干底层事件的组合。比如鼠标拖放(drag-and-drop)多表示移动被拖放的对象,由鼠标按下、鼠标移动和鼠标释放三个底层事件组成。”

好莱坞经纪公司相当于一个背后运作的软件平台,艺人相当于一个callback,‘留下你的电话’就是注册callback,‘我们会打给你的’就是异步调用callback。”

如果将编程譬比命题作文,不用framework的程序是一张可以自由写作的白纸,library是作文素材库;采用framework的程序是一篇成型的作文,作者只须填写空白的词语和段落即可。”

控制反转不仅增强了framework在代码和设计上的重用性,还极大地提高了framework的可扩展性。这是因为framework的内部运转机制虽是封闭的,但也开放了不少与外部相连的扩展接口点,类似插件(plugin)体系

从字面上看,‘回调’强调的是行为方式——低层反调高层,而‘抽象接口’强调的是实现方式——正是由于接口具有抽象性,低层才能在调用它时无须虑及高层的具体细节,从而实现控制反转。”

QQ服务器是事件管理器,每个聊天者既是事件源又是事件处理器,这正是事件驱动式的P2P模式啊[16]。此外,聊天时不等对方回答,就可与另一网友交谈,这就是会话切换带来的异步效果。

事件是程序中令人关注的信息状态上的变化。在基于事件驱动的系统中,事件包括内建事件与用户自定义事件,其中内建事件又分为底层事件和语义事件。此外,事件还有自然事件与合成事件之分。

Callback指能作为参数传递的函数或代码,它允许低层模块调用高层模块,使调用者与被调者从代码上解耦。异步callback在传入后并不立即被调用,使调用者与被调者从时间上解耦。

控制反转一般通过callback来实现,其目的是降低模块之间的依赖性,从而降低模块的耦合度和复杂度。

在框架设计中,控制反转增强了软件的可重用性、柔韧性和可扩展性,减少了用户的负担,简化了用户的代码。

控制反转、依赖反转原则和依赖注射是近义词,它们的主题是控制与依赖,目的是解耦,方法是反转,而实现这一切的关键是抽象接口(包括函数指针、抽象类、接口、C++中的泛型函子和C#中的委托)。

事件驱动式编程的3个步骤:实现事件处理器;注册事件处理器;实现事件循环。

异步过程在主程序中以非堵塞的机制运行,即主程序不必等待该过程的返回就能继续下一步。异步机制能减少随机因素造成的资源浪费,提高系统的性能和可伸缩性。

独立是异步的前提,耗时是异步的理由。

知不知,上;不知不知,病。 ——《老子·德经》

单靠记忆只能触及知识之表,单靠练习只能深入知识之里,唯有培养方能渗透知识之根。

学会适度地容忍无知。

不仅需要强调钻劲和深度的“钉子精神”,还需要强调磨功和广度的“刨子精神”。 编程语言的语法、语义等都是从编程范式的树根衍生而出的枝叶,把握了这种脉络和节奏,代码才会如音乐舞蹈般韵律有致。 每种范式擅长的问题领域不尽相同,只有博闻广识,方可扬长避短,程序才会如行云流水般流畅自然。 程序员更习惯机器风格的过程式思维和现实风格的OOP思维,不容易接纳数学风格的函数式思维。

单靠记忆只能触及知识之表,单靠练习只能深入知识之里,唯有培养方能渗透知识之根。

学习如打仗,除了要有直线式的纵深攻击,还要有曲线式的迂回包抄。

我们必须学会适度地容忍无知。请注意,容忍无知不是放任无知,而是一种学习的技巧,让无知成为求知的动力而不是障碍。容忍无知能使我们既不沮丧气馁,也不急于求成。在学习时不妨略过一些细节或难点,先概览全貌以获取感性认识,然后在逐步积累中升华为理性认识。要而言之,我们不仅需要强调钻劲和深度的‘钉子精神’,还需要强调磨功和广度的‘刨子精神’。

你若不了解圣经文化、不了解文艺复兴史,则欧洲之行至多只是视觉的盛宴,而非文化的洗礼,收获将是有限的,印象将是肤浅的。同样,如果你不了解编程范式,那么眼中的编程语言只是语法、语义、核心库、规范等组成的集合,写出的代码虽能编译、能工作,却会显得生硬、别扭。就像中式英语,语法正确、表达也正确,可就是不正宗、不地道。其症结我们在第一节课中已经提过了,即所谓的语感缺失。”

每种范式都代表着一套独特而有效地解决问题的思想和方法。掌握范式对编程语感的提高至少有两层作用:首先,编程语言的语法、语义等都是从编程范式的树根衍生而出的枝叶,把握了这种脉络和节奏,代码才会如音乐舞蹈般韵律有致;其次,每种范式擅长的问题领域不尽相同,只有博闻广识,方可扬长避短,程序才会如行云流水般流畅自然。”

快速排序法的思想是:在列表中找一个基准元素,将所有小于它的元素划归一个子列,置于其前;将所有大于等于它的元素划归另一子列,置于其后。然后递归地对前后两个子列作同样处理,直至最终。”

命令式编程与声明式编程之间的差别:前者须要指定计算的过程,后者只须指定计算的原则。一个着重微观的细节,一个着重宏观的方向,自有繁简之别。”

把命令式程序比作状态自动机,其运行过程就是在不断地修改机器的状态。而函数式程序则是进行表达式变换,一般不会改变变量的值。其实函数式并非完全不改变内存,只不过改变的是栈内存(stack)罢了。换言之,无副作用函数的作用关键在于其估值结果,按过程式的说法是返回值。刚才的quicksort不正是如此吗?”

首先,没有副作用的函数易于重构、调试和单元测试。其次,代码有效性与函数顺序无关,方便并发处理和优化处理。举个简单的例子,计算两个函数的乘积:f(x)*g(y)。由于无副作用,f(x)和g(y)的估值过程是独立的,估值顺序也不重要,因此理论上可以对二者并行计算。另外,还可利用惰性求值(lazy evaluation)[5]:如果算出f(x)为零,那么不用计算g(y)便可知乘积为零了。”

“钉子精神”固然可贵,“刨子精神”也不可少。

道常无为而无不为。 ——《老子·道经》

评价代码的复杂度,长短只是一个因素。程序员不是打字员,花在思考上的时间和精力远远超过花在键盘上。 算法=逻辑+控制。其中逻辑是算法的核心,控制主要用于改进算法的

评价代码的复杂度,长短只是一个因素。程序员不是打字员,花在思考上的时间和精力远远超过花在键盘上。 算法=逻辑+控制。其中逻辑是算法的核心,控制主要用于改进算法的效率。

在所有编程范式中,函数式与逻辑式与传统思维方式的差别最大,此前的介绍又过于简单,因而今天特意多谈了些。既然有人提意见,那我就适可而止了。最后请允许我画蛇添足:在代表计算机最高水平的人工智能领域中,这两种范式发挥着举足轻重的作用。单凭这一点,它们也是值得学习和借鉴的。

代码的长度不是衡量软件复杂度的唯一标准。其中的逻辑结构越复杂、越微妙、受需求变化的影响越大,软件越难控制和维护。

有别于过程式和函数式,逻辑式没有明显的输入和输出之分。

相对于命令式,逻辑式更简洁、更抽象、更少副作用,不仅能提高生产效率,还能用于快速原型开发。但在运行效率、可掌控性、语言成熟度等方面有所欠缺。另外,因其思维方式独特而鲜为人用,适合基于规则而非基于状态的应用。

形者神之质,神者形之用。 ——《范缜·神灭论》

设计模式好比组合套路,能在一些特定场合下克敌制胜;编程范式则好比武功门派,博大精深且自成体系。 一种编程范式之所以能独树一帜,关键在于它突破了原有的编程方式的某些限制,带来革命性的新思维和新方法,进一步解放了程序员的劳动力。 因其长而容己,因其短而容他,此万物之理也。 语言为形,范式为神。若能以神导形、以形传神,则看似平白无趣的程序也能写出诗画般的意境。

言,“设计模式一般针对某一特定场景的问题,而编程范式针对的是广泛得多的问题领域,通常有一整套的思想和理论体系,具有全局性、系统性和渗透性,这一点在5大重要范式中显得尤为突出。因此,编程范式更普适更抽象,涉及的深度和广度也是设计模式难以比拟的。”

设计模式好比组合套路,能在一些特定场合下克敌制胜;编程范式则好比武功门派,博大精深且自成体系。”

一种编程范式之所以能独树一帜,关键在于它突破了原有的编程方式的某些限制,带来革命性的新思维和新方法,进一步解放了程序员的劳动力。这便是范式的核心价值所在。”

过程式编程的数据与代码脱节,不方便维护;函数式和逻辑式的开发效率一般比过程式高,但运行效率和语言表现力则有所不如;对象式编程用于数学计算、符号处理等对象特征淡薄的领域,在心理上缺乏认知基础,在运行效率上不如纯过程式,在开发效率上不如函数式;并发式编程增加了代码的复杂度,加重了程序员的负担;泛型式编程影响了代码的可读性,过度使用模块还可能造成代码膨胀(code bloat)[18];元编程过于强大,运用不当会超出程序员的控制,宜谨慎使用;切面式编程减少了程序的可预测性和可控性,同时给代码的跟踪调试带来一定困难,还可能造成性能上的损失;事件驱动式编程虽然也能用于同步的流程应用,但毕竟机制更复杂,没有普通的流程式编程那么自然易懂。”

“因其长而容己,因其短而容他,此万物之理也。”

作文绘画讲究形神兼备,编程也不例外。语言为形,范式为神。若能以神导形、以形传神,则看似平白无趣的程序也能写出诗画般的意境。” 一席话说得众人皆觉虽不能至,然心向往之。

相比设计模式,编程范式针对的问题领域更广泛,提出的思想和方法更普适、更抽象、更系统。此外,设计模式重在设计,对语言和工具的要求不高,而编程范式须要建立一套抽象机制和方法体系,离不开语言或工具的支持。

编程范式的核心价值在于:突破原有编程方式的某些限制,带来新思维和新方法,从而进一步解放程序员的劳动力。

理论是认生的孩童,多陪他玩玩,自会活泼起来。 ——题记

所谓包,指函数与其周围的环境变量捆绑打包;所谓闭,指这些变量是封闭的,只能为该函数所专用。

如果把待加工的菜看作数据,技法看作算法,将数据与算法分离,以算法为中心,那是什么范式?” “泛型式!”

闭包是一种能保留当初创建时环境变量的函数。它通常以匿名的方式存在,多用于函数式编程中,能使代码更加简洁清晰。Java中的匿名类可以看作OO化的闭包形式。

每种编程范式都能在生活中找到它的应用,它们本来就是人类思维方式的投影。

褚小者不可以怀大,绠短者不可以汲深。

编程水平的提升之道是:在实战中演练招法,在招法中领会心法,心法反过来提升招法,进而提高实战水平,如此循环往复呈螺旋式上升。正所谓熟能生巧,巧能生通。

迭代学习法:即在具体知识与抽象理论之间进行折返式学习。

无论干哪一行,要想胜任愉快,离不开四样东西:才能、兴趣、方法和努力。

“花拳绣腿对付小喽啰绰绰有余,真碰到高手自然漏洞百出了。”冒号直言道,“编程水平的提升之道是:在实战中演练招法,在招法中领会心法,心法反过来提升招法,进而提高实战水平,如此循环往复呈螺旋式上升过程。正所谓熟能生巧,巧能生通。”

软件工程中有个迭代开发法,本班则采用迭代学习法:即在具体知识与抽象理论之间进行折返式学习。当然这种迭代不是机械式的重复,而是增量式的循环。假定你们以前更关注具体的编程语言,那么遵循这种方式,先介绍抽象的编程范式是合适的。在初步了解范式之后,不妨重新回到编程语言上来。”

一个真正企业级的项目,涉及面太广。比如一个完整的web应用,不论是采用重量级的Java EE或.NET技术,还是采用轻量级的Perl、PHP、Ruby、Python等动态语言技术,除了要掌握各自的主体语言外,还涉及到相应的框架、集成环境和各种工具,以及JavaScript、CSS、HTML和XML等技术,同时数据库的知识也是不可或缺的。”

不妨把接待员看作客户层(client tier),把服务员看作表现层(presentation tier),把厨师看作业务层(business tier),把收银员看作数据层(data tier),把厨工看作常用的辅助类(helper class)[2]。”

“当然程序员会各有分工,不过如果你总局限某一层的应用开发,今后凭什么谋取更高的职位?”冒号苦口婆心,“无论干哪一行,要想胜任愉快,离不开4样东西:才能、兴趣、方法和努力。没有才能则难以胜任,没有兴趣则难以愉快,没有方法则事倍功半,没有努力则一事无成。我相信好的方法最终能激发人的才能、兴趣和努力,这也是本班的一个理念。

先简单谈谈动态语言;再对主流语言作简评;然后范式上以OOP为主,语言上以C++、Java和C#为主,作一些专题讨论;最后如果时间允许,找一个项目来实践一番。”

操纵于规矩之中,神明于规矩之外。 ——《俞震·古今医案按》

数据类型包含两个要素:一个是允许取值的集合,一个是允许参与的运算。例如int类型在Java中既定义了介于-231和231-1之间的整数集合,也定义了该集合上的整数所能进行的运算。

数据类型既有针对机器的物理意义,又有针对人的逻辑意义。前者用于进行底层的内存分配和数值运算等,后者用于表达高层的逻辑概念。既然类型如此重要,类型检查就必不可少了[5]。所谓动态类型语言(dynamic typing language),正是指类型检查发生在运行期间(run-time)的语言。”

“那静态类型语言(static typing language)自然是类型检查发生在编译期间(compile-time)的语言咯。”引号接话道。 冒号回应:“一般的说法是这样,但我更愿意将‘编译期间’4个字改为‘运行之前’,否则容易让人误解为静态类型语言一定是编译型语言(compiled language)。”

一般的说法是这样,但我更愿意将‘编译期间’4个字改为‘运行之前’,否则容易让人误解为静态类型语言一定是编译型语言(compiled language)。”

动态类型语言的确有它的优势:简明、快捷、灵活,并且天然具有泛型(generic)特征。值得一提的是,动态类型有一种被称作鸭子类型(duck typing)的形式。”

如果一个对象既会走鸭步又会呷呷叫,何妨将其视作鸭子呢?”

C++是静态类型语言,但它的模板也可实现类似功能,并不须要引入继承关系。”

“Duck类型的哲学是:名义不重要,重要的是能力,

静态类型语言的好处:由于在运行之前进行了类型检查,一方面代码的可靠性增强,符合‘发现错误要尽早’的原则;另一方面编译器有可能藉此优化机器代码以提高运行效率,同时相比前者节省了运行期的耗费在类型检查上的时间和空间。此外,变量类型的声明彰显了编程者的意图,有辅助文档的功效。”

静态类型检查类似‘疑罪从有’的有罪推定制——在被证明合法之前是非法的,动态类型检查类似‘疑罪从无’的无罪推定制——在被证明非法之前是合法的。至于如何取舍,套用一句话:‘Static Typing Where Possible, Dynamic Typing When Needed’。不妨理解为:尽可能守规则,必要时求变通。”

通常弱类型语言(weakly-typed language)允许一种类型的值隐性转化为另一种类型[7]。举个例子,1+“2”在VB中等于3——第2个字符串转化为整数;在JavaScript中等于“12”——第1个整数转化为字符串;在C中则等于一个不定的整数值——第2个字符串作为地址来运算。这样似乎很有趣很方便,但程序容易藏污纳垢,滋生臭虫(bug)。与此相对地,强类型语言(strongly-typed language)着意贯彻类型控制,为保障数据的完整性和代码的安全有效性,一般不允许隐性类型转换[8]。如果一定需要类型转换,必须是显性转换,一般通过我们熟知的铸型(cast)来完成。”

动态类型的类型检查发生在运行期间,静态类型的类型检查发生在编译期间(运行之前)。

动态类型的变量不须要显式声明,静态类型的变量须要通过显式声明或类型推断。

鸭子类型是动态类型的一种风格,允许非继承性多态,即一个对象的类型可以由其接口集合来确定,不须要通过显式继承。它有利于代码重用,但也可能造成误用和滥用。

动态类型语言的优点:代码简明灵活、易于重用,适合泛型编程和快速原型开发。

静态类型语言的优点:运行之前的类型检查增强了代码的可靠性,使编译器有可能进行优化处理从而提高运行效率,节省了运行期的类型检查所占用的时间和空间,同时类型声明有辅助文档的功效。

静态类型检查实行“疑罪从有”的有罪推定制,动态类型检查实行“疑罪从无”的无罪推定制。取舍的原则是:Static Typing Where Possible, Dynamic Typing When Needed。即尽可能守规则,必要时求变通。

类型的动静以类型的绑定时间来划分,类型的强弱以类型的约束强度来划分,它们之间没有必然联系。弱类型语言允许类型的隐性转化,被认为是类型不安全的;而强类型语言则一般不允许这种转化,被认为是类型安全的。

故凡天下之理,欲向动上求静,静上求动。 ——《蔡牧堂·发微论》

Perl的发明者Larry Wall不愧为语言学家,对此有一个精彩的说法:‘A script is what you give the actors, a program is what you give the audience’。直译为:脚本是给演员看的,节目是给观众看的。此言妙在一语双关——program兼有‘节目’和‘程序’的意思。”

换言之,程序是为终端用户服务的,而脚本是为程序员服务的。”

脚本最常见的形式是壳脚本(shell script),在非Unix类的操作系统中也称为批处理文件(batch file)。”

壳脚本是在壳上运行的脚本,扩展了命令行下可执行的命令。它最初主要是内建(built-in)命令的组合,用于系统程序的调度,是系统管理员的必备武器。

壳脚本是在壳上运行的脚本,扩展了命令行下可执行的命令。它最初主要是内建(built-in)命令的组合,用于系统程序的调度,是系统管理员的必备武器。其后,壳脚本也发展到用于应用程序的调度、连接、调试等,成为粘合(glue)语言。”

脚本语言以语言的实际用途为标志,动态语言以语言的语法特征为标志。”

动态语言能在运行中增加或改变数据结构、函数定义、对象行为或指令流程等。如果说动态类型语言的动态体现在类型上,动态语言的动态则体现在结构和功能上。相比而言,静态语言虽然也可能实现同样的效果,但既不方便也不自然[12]。另外,不容忽视的一点是,动态语言大多是开源的,其本身的发展也更具动态性。”

“动态语言秉承的一个理念是:优化人的时间而不是机器的时间。为提高人的生产率,宁肯牺牲部分的程序性能或者购买更高配置的硬件。由于硬件相对于人件一直在贬值,该理念便有了合理的现实基础。”

大多数动态语言支持eval函数,能动态执行任意字符串形式的代码,并有丰富的反射(reflection)机制,天然支持元编程。

动态语言虽然多为解释型语言(interpreted language),但许多也提供了与Java类似的字节码编译(bytecode compilation),甚至JIT编译(just-in-time compilation)。

为了快速搭建一个系统,以适应不断变化的客户需要,可以先采用开发效率更高的动态语言。在交付时再将其转化为编译型的静态语言。如果系统对性能的要求不高,这种转化至多是局部的。有的干脆一字不易,不仅省了当下的时间,以后维护起来也更方便。”

脚本语言与动态语言尽管并不完全重合,但更多地还是提法上的区别。前者强调作为命令行工具和粘合工具的语言用途,后者强调动态的语言特征。当脚本语言不再局限于粘合语言,从专用语言发展为通用语言,并且胜任复杂的应用开发的时候,动态语言的提法显然更加合理。 ➢ 动态语言能在程序运行期间改变数据结构、函数定义、对象行为或指令流程等,相比静态语言在结构和功能上的更具动态性。 ➢ 动态语言重在优化人工时间而非机器时间,因此相比静态语言,其开发效率较高,但运行效率较低。 ➢ 动态语言的以下特点决定了它在大型应用开发中的价值:代码量较少,从一定程度减轻了维护难度;不少提供了字节码编译或JIT编译,弥补了运行效率上的不足;一些模块的结构和功能上的变化不会导致相关模块的重新编译和连接;具有灵活、适应力强和开发周期短的特点,能快速响应客户需求的变化,并且适合快速原型开发。 ➢ 静态语言安全稳定、性能优越、成熟普及,并且逐渐开始吸纳动态语言的一些优点,这些都决定了它不可能被后者完全替代。

爱而不知其恶,憎而遂忘其善。 ——《吴兢·贞观政要》

理想的架构师应当如文学大师,既有恢弘大气的构思,又有细腻深刻的笔法;应当如统军大帅,既有运筹帷幄的韬略,又有冲锋陷阵的武功。

从心理学上分析,一个人在某种观点形成后,会通过自我的暗示和倾向性的证据不断地强化这种观点,并对其他观点本能地选择性失明。这背后折射的其实是一种懒人心态——认定自己掌握的语言是最好的,便不必费事再学其他语言了;这更是一种弱者心态——无论是耿耿忠心的铁杆卫士,还是振振有词的辩护律师,一旦丧失自我批判的勇气和精神,声嘶力竭的挞伐只能反证他们的偏狭浅薄与自信缺失。”

每种语言都有天然设计上的不同,当你用得很别扭时,恰恰说明没有真正掌握。正如刀法以劈为主,枪法以扎为主,你若反其道而行之,刀扎枪劈,能不别扭吗?如果愣拿双节棍当单节棍使,恐怕没砸到别人倒先砸晕自己了。可见不是兵器问题,而是招法问题。”

C++的发明者Bjarne Stroustrup说过这样一句话,语言只有两种:一种怨声载道,一种无人问津(There are only two kinds of languages: the ones people complain about and the ones nobody uses)。”

Stroustrup认为Simula太慢、BCPL又太底层,于是发明了C++;Gosling觉得C++用得不爽,于是发明了Java;微软恼恨Java,于是请Hejlsberg发明了C#;Matsumoto用了两年多的C++仍不顺手,于是发明了Ruby。如此这般,不一而足。”

从另一个角度看,发明一种语言也是对先前语言的一种最高赞美。C++之于C,Java之于C++,C#之于Java,都是后者对前者的一种承认,哪怕是极不情愿的承认。批判与赞美,继承与发展,谓之扬弃。”

其实C++的真正来源是Simula而不是C,向下兼容C只是因为C效率高、普及广。换句话说,C++与C形同而神异。学会了C当然对C++有一定帮助,但若不能理解C++的OOP思想,那么C的背景反而是一种障碍。”

对一个程序员而言,编程语言乃立身之本。许多人偏偏本末倒置,常常为在IDE、框架、设计工具等中挖掘到某些新功能而欣喜不已,或者津津乐道于各种语言的优劣高下,却对正在使用的语言中大量的宝藏视而不见,与执金碗而行乞者何异?这些人若有幸拜关公为师,他们最艳羡的一定他的赤兔马和青龙偃月刀,或许还会抹红脸蓄长须什么的,就是不太愿学他的盖世武功。”

破除语言的宗教情结,保持自我批判的勇气和精神。

语言不等于低级,设计也不等于高级。

居高者形逸而神劳,处下者形劳而神逸。 ——《洪应明·菜根谭》

C++在过程式的基础上又引入对象式和泛型式,同时保持了C的高效性和底层开发能力。”

Java既继承了C++的优点,又克服了C++的复杂性,虽然底层开发能力有所减弱,但具备平台无关性。”

C#兼具C++与Java各自的优点,但效率上不如C++,跨平台方面不如Java。”

语言,它源自Unix操作系统的开发,以其良好的抽象性和可移植性取代了汇编语言作为系统开发语言。因其简洁实用、灵活高效,很快从系统领域发展到其他领域而成为通用语言。随着新兴语言的崛起及硬件性能的大幅提高,C语言的缺点也日益显著:过于宽松的类型检查、容易出错的内存管理、相对贫乏的语言特征,等等。虽然自身还在发展,它的市场份额日益减少乃是不争的事实。但在相当长的时间内,它在其所擅长的领域里仍会占举足轻重的一席之地。

说C语言,它源自Unix操作系统的开发,以其良好的抽象性和可移植性取代了汇编语言作为系统开发语言。因其简洁实用、灵活高效,很快从系统领域发展到其他领域而成为通用语言。随着新兴语言的崛起及硬件性能的大幅提高,C语言的缺点也日益显著:过于宽松的类型检查、容易出错的内存管理、相对贫乏的语言特征,等等。虽然自身还在发展,它的市场份额日益减少乃是不争的事实。但在相当长的时间内,它在其所擅长的领域里仍会占举足轻重的一席之地。如果C能借鉴C++的命名空间、重载、异常处理和STL等非OOP的特征,它的生命力绝不会比任何OOP语言弱。附带说一句,C虽然没有直接支持OOP的语法,但经过适当的设计还是能实现OOP的[2]。”

“精读K&R的《The C Programming Language》,此书不过200多页,堪称C语言的剑诀。其中的R即Dennis Ritchie,是C的创造者,同时也是Unix的缔造者之一,是真正的大师。如今的大师,同博士、教授、院士等头衔一样,严重地通货膨胀了。”冒号不无感慨。

C++的开发效率相比Java与C#,差距主要在两个方面:一是标准库不够完善,二是须要手工回收垃圾。关于前者,的确是C++的一大软肋,标准库竟然连企业应用中最常用的图形界面、网络编程、数据库处理、多线程等都不能涵盖,严重障碍了生产力。其实C++也有苦衷,不像Java和C#那样有大公司的鼎力支持,只靠效率极为低下的标准委员会来维护。1998年的一个标准直到2003年还在修订,下一个标准至少要到2009年。

在C++中,程序员也不是非得手工清理垃圾不可的。更好的办法是遵循RAII的惯用法(idiom)[3],通过智能指针(smart pointer)来解决内存释放

C与C++可以说是成也指针,败也指针。用得好可以是削铁如泥的神兵利器,用得不好则可能是自我毁灭的罪恶渊薮。但由于二者定位于系统语言,而指针对于底层操作是必不可少的。同样道理,二者的数据类型的转换比其他静态类型语言更自由,也是源出于此。”

指责C++不是100%OOP的说法更是荒谬至极。OOP又不是金子,含量越高越好。试图把一切都装进OOP的箱子里的想法无异于削足适履。

既然系统语言主要为底层系统的开发服务,这就决定了它们的理念是:优化机器的时间而不是人的时间,优化机器的记忆而不是人的记忆;假设编译器是愚蠢的而程序员是聪明的,因此赋予程序员更多的权利、义务与责任。无视这种背景和理念而去与其他语言相较,完全是不着筋节,不值一哂。

常见的‘C/C++’的说法很不科学。C与C++虽有千丝万缕的联系,但一个简单,一个复杂;一个纯过程式,一个集过程式、对象式、泛型式和元编程于一体。貌合神离,不宜混为一谈。”

学好C++即使推荐十本仍有遗珠之憾。可以说C++是苦了编程者,甜了著书人。开个小书单:初级——《C++ Primer》和《Thinking in C++》;中级——《The C++ Programming Language》和《Effective C++》系列;高级——《The C++ In-Depth》系列。这里还要特别推荐一下《The Design and Evolution of C++》,从中你可以看到C++的设计和演变的来龙去脉,极具启发性。其他的C++精品书籍还有不少,恕不一一列举了。C++是匹无辔无鞍的野马,看似桀骜不驯,若能顺性而御,必能足踏飞燕,行千里而不劳。”

C++对C语言的兼容是其成功的一个重要因素,但同时也继承了C的一些缺陷。

C++设计者没有直接支持自动垃圾回收,是担心它造成过大的时空开销,同时会削弱底层开发能力。

D语言提供了可控制的垃圾回收器,支持线程同步、动态数组、嵌套函数和契约式设计,并废除了头文件和前置声明(forward declaration)。

程序的性能和效率永远是重要的。一方面,用户对软件性能的期望越来越高;另一方面,有时硬件性能与软件需求并不匹配:有些应用(如人工智能、大型计算)对程序的性能和效率要求极高,有些系统(如嵌入式系统)的资源十分有限。

大巧在所不为,大智在所不虑。 ——《荀子·天论篇》

Java的目的是让一种语言在多种平台上运行,而C#(.NET)的目的是让多种语言在一种平台上运行。

两个平台语言(Java和C#),一个重在语言,让语言向平台扩散;一个重在平台,让平台来凝聚语言。 C++提供了一马平川的大路,也提供了陷阱密布的小道;Java则在大路上铺设水泥,同时封锁了捷径小道;C#同样填平了大路上的坑坑洼洼,但把一些小道上“此路不通”的牌子悄悄换成了“此路危险”

Java运行于以Java Virtual Machine(JVM)为核心的Java平台,而C#运行于以Common Language Runtime(CLR)为核心的.NET平台。”

Java可通过JNI调用C、C++等程序;C#可通过P/Invoke访问系统API,加之保留了指针操作,用于系统编程更加方便。其实一种语言胜任多种领域并不难,难的是在每个领域都独占鳌头。

“多学习、多实践、多体悟,秤的精度自然会提高。”冒号鼓励道,“如果自己没有足够鉴别商品的能力,建议从商家的竞争对手那里获取真实的信息。要看Java的优点,不要听Sun的一面之词,可以看看微软的C#模仿了Java的哪些特征,因为模仿是最高的赞扬;要看Java的缺点,可以看看C#摈弃了Java的哪些特征,可以听听微软对Java的批判——虽然可能过激,但一般也不会太离谱。此法同样适用于包括C#在内的其他语言。”

更加安全——限用或禁用指针;检查字符串和数组的边界;增加类型安全;禁用未初始化的变量;增加了对资源的安全管理; 更加简单——自动垃圾回收;废除了头文件和宏(macro);支持接口(interface);废除多继承及非公开继承;摒弃了C++中一些生僻、难用的语法; 更加中性——编译成机器无关代码;运行于统一平台; 更加OO——不支持全局变量和自由函数;所有的类都可上溯到Object类;对象一般通过new分配在堆(heap)上; 更加丰富——支持反射(reflection);支持并发编程;提供图形界面、网络编程、数据库、XML等API; 更加标准——更好的Unicode支持;支持注释性文档;类或方法可以被废弃(deprecated)。

在Java1.5的增强特征中,有不少C#的影子。如增强版的for循环;可变长参数(varargs);自动装箱(autoboxing);类型安全的枚举(typesafe enum),等等。此外,支持元数据的的注释(annotation)与C#的特性(attribute)大同小异,C#从Delphi中引入的属性(property)也出现在Java 7的提案中。”

Java的目的是让一种语言在多种平台上运行[10],而C#(.NET)的目的是让多种语言在一种平台上运行[11]。”

总而言之,每种语言都为解决问题提供了一定的途经,C++、Java和C#的不同之处在于:C++提供了一马平川的大路,也提供了陷阱密布的小道;Java则在大路上铺设水泥,同时封锁了捷径小道;C#同样填平了大路上的坑坑洼洼,但把一些小道上‘此路不通’的牌子悄悄换成了‘此路危险’。”

相比C++,Java与C#更加安全(限用指针、数组边界检查、类型安全、资源管理等)、简单(自动垃圾回收、废除多继承和头文件等)、中性(编译成机器无关的字节码,运行于虚拟机)、OO(无全局变量和函数等)、丰富(支持反射和并发编程、更完备的API等)、标准(注释性文档、更好的Unicode支持等)。

编程语言的发展是技术和商业合力推动的结果。

世人反不难而易之,用是通者亦罕。 ——《欧阳修·诗解统序》

前台编程涉及面专,绚丽花哨的界面更容易让初涉编程者产生成就感;后台编程涉及面广,需要深厚的技术积累和缜密的设计思维;底层编程涉及面深,给人神秘莫测之感。然大道相通,难者亦易,易者亦难。设计一个用户友好的界面与设计操作系统的一个模块,一边是与人直接打交道,一边是与机器直接打交道,孰难孰易、孰轻孰重,焉能一概而论?”

前台编程涉及面专,更关注界面设计;后台编程涉及面广,更关注业务逻辑;底层编程涉及面深,更关注系统资源。它们只是侧重点有所不同,并无真正的难易之别、高下之分。

操千曲而后晓声,观千剑而后识器。 ——《文心雕龙·知音》

Perl凝练晦涩,Python优雅明晰,Ruby精巧灵动,PHP简明单纯。

其实Perl、PHP、Python和Ruby都有自己的虚拟机(virtual machine),从这种意义上说它们也可作为平台语言。但在实际应用中,它们没有Java平台和.NET平台那种整合凝聚力和核心作用,通常作为轻量级的解决方案。”

“世易时移,殊难逆料。但有一点可以肯定,语言的发展趋势一定是动静结合、刚柔并济。”冒号断言,“一方面以Java和C#为代表的静态语言中嫁接了动态语言的枝条;另一方面以Java和.NET为代表的平台与动态语言的交壤地带也在逐步扩大。比如JRuby允许Ruby与Java之间互相调用,类似的还有Jython、IronRuby、IronPython,等等。此外值得一提的是,动态语言最活跃的舞台当数LAMP,L-A-M-P。”

Perl凝练晦涩,Python优雅明晰,Ruby精巧灵动,PHP简明单纯。先看老大哥Perl,它博采众家之长,综合了C语言的结构、sed的正则表达式、AWK的关联数组(associative array)、Lisp的表(list)和Unix Shell的命令,此外还有借鉴了一种语言,

许多人抱怨Python中的自引用self太多,殊不知这也是它倡导显式表达的一种体现。总的看来,Python主要的问题还是在性能效率上不尽如人意。”

体悟愈深者编程语感愈强,思维与语言愈交融无碍,渐从必然王国走向自由王国。”

叹号——没有最好的语言,只有最合适的语言。 逗号——没有糟糕的语言,只有糟糕的程序员。 问号——没有一种语言是万能的,只会一种语言是万万不能的。 引号——废除对语言的宗教信仰,建立对语言的哲学思维。 句号——编程就是在人脑和电脑之间寻找最佳平衡点的过程。

比起Java平台和.NET平台,动态语言轻便灵活、开发效率高,但整合凝聚力还不够,在运行效率、类型安全、可用资源、开发工具、技术支持及影响力等方面也有一定差距,故通常作为轻量级的解决方案。 ➢ LAMP是由Linux、Apache、MySQL和包括PHP、Perl、Python或Ruby在内的脚本语言组成的网络开发平台,具有开放灵活、开发迅速、部署方便、高可配置、安全可靠、成本低廉等优点。 ➢ Perl精练、复杂、强大、灵活、自由、隐晦、表现力强,但规范性、可读性、一致性、整洁性和可维护性较差。 ➢ Python优雅规范、简洁明晰、易学易用、类库丰富,但效率稍差,有些人不喜欢它对空白符敏感的特性。 ➢ Ruby语法精巧、高度灵活,兼具Perl的表现力和Python的可读性,尤其注重程序员的感受,但其性能和线程模型尚有待改进。 ➢ PHP简单、专一、实用、流行,在但相比其他3种语言,在语法和功能上稍有欠缺。 ➢ RoR是一种轻量级套餐式的web应用解决方案,是由好的设计(MVC架构和CoC、DRY原则)加上好的语言(Ruby)在好的时机(web2.0和敏捷开发风行之际)打造出的好的框架。 ➢ 静态语言与动态语言从语言特征到运行环境都在逐渐融合。 ➢ 程序员应该与程序语言一样,既要有自己的专长,又要向通用化和全能化发展。 ➢ 编程语言惯例用法、哲学理念和编程范式形成了语言的编程风格。

打赏作者