转 http://blog.csdn.net/hongbomin/article/details/6979314
3.1 语言的动态特性
介绍动态语言之前,首先介绍一下语言的动态特性,并引出动态语言的定义。
语言的动态特性表示语言具有在运行时确定绑定关系的性质。实际上,“静态”是一个相对的概念,任何程序设计语言都可以看作具有某种程度的动态特性。一个变量能够在运行时改变自身的值,可以说它具有动态特性,比如最简单的赋值语句:
char ch;int m;
ch = cin.get();//从输入流中接收一个字符
m = func(); //将func的返回结果赋给m
对字符型变量ch与值的绑定关系要推迟到程序运行时检测到标准I/O流的输入数据才能确定下来,整型变量m的值也要到程序运行时执行了函数func后才能确定。在运行时进行的动态类型检查、动态内存分配都是语言的动态特性的具体表现。变量的寻址方式也是一个动态的过程。程序经编译后,获得了变量的相对于程序代码段(CS)或数据段(DS)的偏移量信息,在程序执行时,操作系统首先将程序装载到内存中足够大小的某个单元中,这个单元的首地址加上变量的偏移量就形成了变量的存储单元地址。此外,面向对象语言中运行时多态性是一个重要的动态特性。从这个意义上说,早期Fortran语言、C、Pascal都具备一定程度的动态特性,但比较弱,而Smalltalk、Lisp、Python等语言在运行时可以改变变量的类型甚至时自身的程序结构,我们就认为这样的语言具有更强的动态特性。
不同的语言具有不同程度的动态特性。纵观程序设计语言的发展史,如果将Fortran和Lisp语言看作是语言在动态特性上的两个极端的话,那么现代大多数语言都是介于二者之间的折中,综合考虑了开发效率和执行效率以及其他众多因素。各种语言也在相互借鉴,不断演变。下图是以Fortran和Lisp为两头,比较了几种语言的动态性程度:
在上图中,从左到右语言的动态特性逐渐加强。处于最左端的Fortran语言不支持堆栈,所有的变量和子程序都是在编译时分配好内存的,不能进行动态内存分配,因而不能进行函数递归调用,许多问题的解决方式受到极大的限制。这主要是由早期Fortran语言的设计目标决定的,早期Fortran语言主要是为了解决科学和工程中的计算问题,优先考虑的是语言的执行效率。虽然Fortran语言被无数次认定为过时,然而它仍然继续发展着。 Fortran90大大扩展了传统Fortran的功能,增加了现代高级语言的数据和控制特征,允许内存的动态分配,使得它具有和C和Pascal语言相当的能力。
比Fortran动态性更强的C/C++语言提供了指针,支持堆栈,提供了malloc/new和free/delete操作,运行时可以动态分配和释放内存,可以比较灵活地动态生成对象并分配存储空间。此外,C++语言中的RTTI(RunTime Type Identification)机制可以在只有指向某个对象的基类指针的情况下,根据驻留在虚函数表中的类型信息,在运行时确定该对象的准确类型。然而,C/C++程序中的变量类型仍然需要在编译时确定下来,大部分类型检查也是在编译时完成的,执行前完成了大部分的绑定工作。
现在流行的 Java和C#语言的动态特性之所以要比C/C++强,是因为Java/C#提供了更强的反射Reflection机制,可以在运行时通过 Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现的interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤醒methods。如在C#中,可以通过System.Type类在运行时获取任何给定数据的类型信息,包括构造函数、属性、方法和事件,也可以唤醒(Invoke)相关的方法(具体的使用可参考Microsoft出版的《Inside C#》的“Querying Metadata with Reflection”一章)。
而Smalltalk以右的语言,变量的类型绑定和类型检查都是在运行时进行的。Perl、Python和Ruby语言可以在运行时修改类的结构或定义,变量的类型可以按需改变,编写程序时无须声明变量类型。Lisp语言甚至可以在运行时动态地改变自身的代码, Lisp语言对程序代码和数据一视同仁,都看作是存储在内存中的对象,这样数据结构既可作为程序执行,程序也可作为数据进行修改。
上面介绍的几种语言都具备一定程度的动态特性,那么应该如何定义动态语言呢?目前在许多开发社群中,提及“动态语言”,一种普遍的观点是认为“动态语言是指能够在运行时改变程序结构和变量类型的语言”,有时也称作“动态类型语言”(Dynamic Type Language),本文采用的就是这种观点,在下文中的动态语言都是指具有这种性质的语言,与之相对的概念“静态语言”指的是在编译阶段确定变量或数据对象类型的语言。按照这种说法,上图中Smalltalk/Perl以左的为静态语言,以右的语言都为动态语言。这里需要说明的是,许多技术文章和语言研究中都涉及到“脚本语言”的概念,实际上脚本语言就是动态语言,因为脚本语言也具备在运行时动态改变类型的性质,符合动态语言的定义,本文不区分这两个概念,一律使用“动态语言”,强调该类语言的最明显的特征。另外,有的人也将“静态语言”称之为“系统程序设计语言”,因为大多数静态语言都是系统程序设计语言,这两个概念只是从不同的角度描述了同一类语言,各自的侧重点不同,“静态语言”的“静态”是基于绑定时间的,强调了语言中大部分绑定关系是发生在运行前,而“系统程序设计语言”强调语言所能完成的功能是侧重于编写系统程序。
3.2 动态语言的特点
近几十年来,传统的软件开发模式和工具、编程语言已难以适应软件需求的变化,软件的混合式开发逐渐增加。随着动态语言的兴起,人们的编程方式和观念发生了转变。在诸如C/C++、 Java、C#之类的静态语言中,语法规则迫使程序员在使用一个变量前首先要声明它的类型,变量的类型一旦确定下来,那么在整个程序中都不能改变,只能是一种类型。而在象Smalltalk、Python、Perl、Tcl、Ruby、Visual Basic和Unix Shells的动态语言中,无类型化定义使程序员从庞大的类型系统解脱出来,变量不经声明就可以直接使用,变量的类型可以按需改变,编写程序变得轻松自由。本节主要介绍动态语言的特点和编程方式,以及与静态语言的主要区别和联系。
动态语言,顾名思义,最显著的特点就是在于它的“动态性”,即运行时可以按需改变程序结构和变量类型、函数的定义等,这也是与静态语言的根本区别。除此之外,动态语言还有许多特点,这些特点深受开发人员的喜爱,使得动态语言渐入人心。
3.2.1 动态类型
类型系统影响了语言的诸多特性。在静态语言中,声明的目的是为了告诉编译器所使用的变量“是什么”,使类型检查可以在编译阶段静态地进行,尽量减少执行时的类型检查,提高执行效率,但缺乏灵活性。而在动态语言中,没有声明语句,赋值语句将名称绑定在数据对象上,如果名称赋予数据对象一种类型,稍后也可以给对象赋予另外一种类型。变量被设计成无类型的,变量的类型可以按需改变,同一个变量既可作为整型的,也可作为字符串,还可用来定义函数。
我们知道,程序中定义的操作一般需要特定类型的参数作为操作的输入,操作只有在接收到类型正确的参数时才能正确无误的执行,最典型的实例就是函数的定义,函数的原型包括函数的参数列表和返回值类型,参数列表提供输入参数的全部信息,执行函数前首先要进行参数类型的检查。在动态语言中,变量是无类型的,那么如何保证所执行的操作是否接收到类型正确的参数呢?在运行时进行动态类型检查机制解决了类型安全这一问题。动态类型检查通过在每个数据对象中保存一个类型标签表明该数据对象的类型,比如在表达式C=A+B中,A和B的类型在程序运行时确定,也可以在运行时改变,所以每次执行 + 操作时都要根据类型标签对A和B的类型进行检查,只有在类型正确的情况下才能执行,否则发出错误信号。操作正确执行后,确定了变量C的类型,并记下C的类型标签以备随后可能的操作进行类型检查。
显然,动态类型检查不能静态地检测到程序代码中类型不匹配的错误,并不意味着动态类型容易在程序中引入类型安全的错误。事实上,在诸如C++、Java之类的静态类型语言中虽然能够在编译时尽可能多的检测到程序中类型失配的错误,但类型仅仅是数据的一小部分信息,类型正确并不能保证程序中不存在其他的错误。在大规模的程序中,要为类型上编写大量的语句,这就使得程序员专注于程序中的类型正确性而容易忽视程序其他部分的正确性。有些问题用静态类型很难实现,例如对于一个不支持泛型编程的语言来说实现一个可以支持多种类型的容器类就比较困难,假如编写了一个可以存放objects的容器类,在具体应用时要通过向上或向上转换成所需的类型,而这种转换往往是不安全的。而动态类型语言不需要这种转换,它所实现的容器类完全是泛型的,在语言一级就提供了良好的支持[3]。裘宗燕教授则认为“很多人之所以在最初使用具有丰富动态特征的语言编程时容易犯错误,主要是因为他们习惯了诸如C、Pascal等语言,不了解用这些动态特征丰富的语言中编程的一些必要的风格和习惯。如果熟悉了这些风格和习惯,犯错误这件事情同样是可能很好地避免的。使用弱类型的语言同样可以开发很好的系统,而且实际上已经开发了很多很好的系统。”[6]。
动态类型检查的主要优点在于程序设计的灵活性,不需要声明语句,一个变量名绑定的数据对象的类型可以在程序执行时按需改变,使程序员从数据类型摆脱出来,同时也可以编写更少的程序代码行完成同样的功能。动态类型是动态语言的最显著的优点,但也是动态语言的弱点根源所在。运行时进行的类型检查也存在几点重大不足:
a. 程序难以调试。因为动态类型检查只在程序运行到某一条操作时才对其进行类型检查,而ü 从来不ü 检查没有被执行的执行路径上的操作。在软件测试时是不ü 能遍历程序中所有的执行路径,ü 这样没有被执行的路径仍有可能存在bugs。这一点可能是动态语言致命的缺点,它导致了动态语言对开发大型软件项目支持力度不够。
b. 保存大量的类型信息。运行时需要相当大的额外存储空间。
c. 执行效率低。动态类型检查要靠软件模拟实现,主要是在运行时完成的,所以在执行速度上降低了不少。
静态类型和动态类型都有各自的优点和缺点,不能简单地认为静态类型比动态类型要好或者动态类型比静态类型要好[3],关键要看具体应用的场合。
3.2.2 开发效率高
在动态语言社区里,值得开发人员津津乐道的是动态语言具有比静态语言高数倍的开发效率。虽然静态语言在执行效率上比动态语言略胜一筹,但在开发效率上只能甘拜下风。正如前文所提到,程序设计语言逐步从面向机器到面向人过渡,以牺牲运行效率来换取开发效率。如今,计算机硬件性能的大大提升,我们能够承受起动态语言在执行效率上的损失,实际上,只有很少的应用才需要尽可能的利用机器的性能。目前,普遍认为Python的开发效率要比Java高出5-10倍。那么动态语言为什么会在开发效率方面具有天独厚的优势?下面就从几个方面进行说明:
a. 易学易用,编程人员可快速上手
Python、 Perl、Ruby等动态语言具有简洁的语法规则,交互式的编程环境,比C++、Java等静态语言更容易学习和掌握。比如Python语言,语言的语法结构、控制结构和C/C++十分类似,对于一个熟悉C、C++语言的编程人员来说,花3-5个小时完全可以掌握Python的语法特性。软件的复杂性在于整合了千丝万缕的关系,交互式环境把程序分解成一块块,对于不熟悉的语言特性,可以方便输入交互式命令进行测试。动态语言不需要声明语句的编程方式使得动态语言更加接近于自然语言,简单明了,易学易读,许多优秀的语言特性都是语言大师深思熟虑之后的产物。对于一个软件项目来说,项目经理更愿意选择一门易于上手又同样可以完成任务的编程语言作为软件的开发语言,一则可以减少员工的学习成本,二则有利于缩短项目完工的时间。
动态语言的易学易用性最终导致了编程群体的改变。二十年前大多数编程者是大型项目的专业编程人员.那个时代的编程人员需要花几个月的时间掌握一门语言和它的编程环境,系统程序设计语言(静态语言)就是为这些人设计的.然而,自从个人电脑出现以后,越来越多的非专业编程者加入到编程者的行列.对这些人来说,编程不是他们的主要工作,而只是他们偶尔用来帮助他们工作的工具。偶尔编程的例子是编写简单的数据库查询或者是电子数据表的宏.非专业编程者不希望花几个月的时间学习一门专为系统程序而设计的静态语言,但他们可以花几个小时的时间学到足够的动态语言知识来写出有用的脚本代码。由于动态语言有简单的句法并且省略了诸如对象和线程的复杂特性,因而它比静态语言要更容易学习和掌握。例如,非专业编程人员很少会选择Visual C++,而大部分会用Visual Basic编写有用的应用程序[1]。
b. 内置丰富的数据结构和操作
实际上,动态语言是建立系统程序设计语言之上的语言,大多数动态语言解释器的内核都是用某种静态语言实现的,Python是用C实现的,JVM上的Jython是用纯Java实现的,.Net CLR上的IronPython是用C# 实现的。动态语言将灵活性作为其设计的目标,优先考虑了语言的灵活性。因此,动态语言在语言层次上集成了许多操作方便、性能良好、高度抽象的数据类型,为编程人员提供了高效的数据结构实现,有助于提高开发效率。以Python语言为例。Python语言提供了强大的数学运算,可以处理超大的整数和浮点数,支持复数的基本运算。Python内置了功能强大、使用方便的数组类型,程序员不需要预先声明使用数组的大小就可以直接使用数组,也不必担心数组大小不够,这些解释器会自动根据具体地操作动态地分配数组的存储空间,不象C/C++中使用数组首先必须声明数组的大小,这就使得很多人在使用数组时不知道究竟应将大小设置成多少,大了觉得浪费,小了又担心不够,想改成指针方式动态分配又有可能给程序带来新的问题。对数组类型还有一系列灵活的操作,比如可以在数组任何位置插入元素,可以对数组分片,可以将数组的某一片作为参数传递给某个函数而并不需要将数组从头到尾的都传递。在Python的具体应用中使用可能最广当属字典类型(Dictionary Type)。字典类型是Python内置的高效强大的数据类型,是一种类似于关联表和哈希表的结构,Python的实现者花费了大量的时间对字典类型做了优化,以尽可能保证语言的高效性。Python内部实现也使用了大量的字典类型。如果没有提供这些内置的数据类型,开发人员可能需要独立开发出支持所需的数据类型的库程序,而开发这样库不但费时,而且在性能优化和接口通用性方面欠佳。正是因为动态语言内置了丰富的数据类型,节省了开发人员独立实现这些数据类型的时间,从而提高了程序的开发效率[4]。
c. 无类型化,用更少的代码可完成同样的工作
动态语言的动态类型使得编写程序时不需要声明变量的类型,变量无类型化省去了程序代码中大量的编译器编译时所需的类型信息语句,使程序看上去简洁明了。
比如在Tcl语言中,如命令:
button .b -text Hello! -font {Times 16} -command {puts hello}
创建了一个新的按钮来显示16点Times字体,当用户敲击控制键时显示一段小信息。它把六种不同事件类型混合成一条语句:一个命令名(button)、一个按钮控制(.b)、所有权名字(-text, -font, 和-command),简单字符串(Hello! 和hello),包含字样名(Times)及字点大小(16)的字体名(Times 16)和Tcl脚本(puts hello)。Tcl统一用字符串表示了这些类型。
上述例子在Java中要调用两个方法完成,需要7行代码。用C++和微软基本类(MFC)需要三个过程,25行代码,在微软基本类中仅仅设置字体就需要几行代码:
CFont *fontPtr = new CFont();
fontPtr->CreateFont(16, 0, 0,0,700, 0, 0, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH|FF_DONTCARE, Times New Roman);
buttonPtr->SetFont(fontPtr);
大部分代码是由静态类型造成的。为了设置按钮字体,必须运用SetFont方法,但这个方法必须通过指针传给CFont对象,这反过来需要声明和初始化一个新的对象.为了初始化CFont对象必须唤醒它的CreateFont 方法,但CreateFont有一个需要14个特殊化引数的固定接口。在Tcl中字体(Times铅字,16点)的基本特征不用声明或转换就可以立即使用。另外,Tcl允许在创建按钮的命令中直接包含按钮行为,而C++和Java中需要把它放在单独声明的方法中。
高级程序设计语言的高级性在于它的每一条代码可以完成更多的工作,完成同样的工作,使用的代码愈少,表明语言的等级愈高。每行汇编代码平均可以翻译成1-3条机器指令,与此相比,每行 C/C++代码平均翻译成5条机器指令,而对于Python、Perl、Tcl这样的动态语言,每行代码可翻译成100-1000行机器指令。汇编程序中,基本上是没有类型,所有的数据看起来都是一样,机器的每个细节都暴露在代码中,程序员必须显式地管理寄存器和内存分配、栈调用等等,所有的操作都涉及到最底层的设备操作,对程序员的专业素质要求高,用汇编语言开发程序效率低下,因此难以编写和维护大型程序。到高级语言出现后,编译器隐藏了机器的底层细节,能够自动管理存储分配、目标代码生成和优化、过程调用顺序等操作,有了抽象数据类型,将程序员从面向机器的复杂中解脱出来,提高了程序的开发效率。
动态语言把许多工作都交给解释器去完成,程序员专注于自己需要解决的问题,建立问题解决方案的逻辑就够了,因此更接近于自然语言。比起静态语言,动态语言的一条语句可以完成更多的功能。从这个意义上说,动态语言是一种更高级的语言。因此,动态语言具有更高的开发效率。
3.2.3 支持动态重构
随着软件应用面临的问题多样化和复杂化,有些应用领域中逐渐出现这样一类需求:要求软件在运行时还能够改变,即动态重构。比如在一个银行系统中,要求软件一旦运行,就不能停下来,否则会对银行造成灾难性的损失,那么这样的系统中如果在运行时发现某个错误怎样修正呢?显然,用静态语言很难实现,因为静态语言在运行时不能修改自身代码,用静态语言实现的系统执行的是经过翻译处理后的可执行代码,要修正软件必须要经过修改源代码->编译->连接 ->生成可执行程序的步骤。而动态语言比较容易实现,动态语言是解释执行的,运行时还能够修改自身的代码。国外有人用Smalltalk实现了这样一个分布式的银行系统,银行系统留有一个接口,开发好的系统发现bug后,维护人员可以通过该接口远程登录后,可以在系统不停止运行的情况下把bug消除掉。
3.2.4 胶水语言(Glue Language)
动态语言一个广泛的应用就是作为“胶水语言”胶合用静态语言编写的组件,从而整合成一个应用程序,这种应用称之为混合式应用。RedHat Linux的安装程序就是通过Python胶合各个组件模块实现的。静态语言开发的程序运行速度快,但开发周期较长,而动态语言灵活简洁,开发效率可提高 5-10倍,但程序运行慢,混合式应用合理结合各自的优点,充分利用不同语言的各自优势,取长补短,可以快速高效地构建应用程序并具有相当的性能。[8]
这里介绍动态语言具有静态语言所没有的优点并不是意味着动态语言要替代或推翻静态语言,而是作为静态语言有力的补充。当然,动态语言不能回避的就是它与生俱来的缺陷,比如执行效率低下、对大型软件开发的支持力度不够等等,使得动态语言难以成为主流的开发语言。现代软件面临的问题越来越复杂,软件工程领域中没有“银弹”可用,同样,也没有一种语言可以成为软件开发的“万金油”,可以很好地解决各类问题,每种语言都各有所长,也各有所短。动态语言和静态语言相互补充,相得益彰,扬长避短,才能充分发挥不同语言的优势,在软件开发中做到“动中有静,静中有动,动静结合,各司其职”。
3.3 动态语言的发展
近几年来,动态语言的发展势头良好,受到越来越多的关注,开发社区显得十分活跃。尤其是Internet的发展,为动态语言提供一个良好的展现个性的活动舞台。Perl因为编写CGI而变得流行,VBScript和JavaScript因为能编写网页而在互联网上广泛使用。
目前,动态语言已经进军当前主流的Java VM/.Net平台。Jython是Python语言在Java VM上的完全实现,是用纯Java语言编写的,可以轻松的访问所有的Java类库。Groovy也是Java VM上一款非常流行的动态语言,并作为JSR草案正式提交给标准化组织审议。IronPython是.Net平台上Python的高效实现。
动态语言的应用已经深入到系统脚本、服务器程序、游戏开发、数据库引擎和CAD系统的开发中,我们有理由相信,随着Internet的发展趋势和混合式应用的扩展,计算机硬件的快速发展,动态语言将会展现蓬勃的生命力,与静态语言一同构建强劲的编程环境,在软件开发中扮演越来越重要的角色。
3.1 语言的动态特性
介绍动态语言之前,首先介绍一下语言的动态特性,并引出动态语言的定义。
语言的动态特性表示语言具有在运行时确定绑定关系的性质。实际上,“静态”是一个相对的概念,任何程序设计语言都可以看作具有某种程度的动态特性。一个变量能够在运行时改变自身的值,可以说它具有动态特性,比如最简单的赋值语句:
char ch;int m;
ch = cin.get();//从输入流中接收一个字符
m = func(); //将func的返回结果赋给m
对字符型变量ch与值的绑定关系要推迟到程序运行时检测到标准I/O流的输入数据才能确定下来,整型变量m的值也要到程序运行时执行了函数func后才能确定。在运行时进行的动态类型检查、动态内存分配都是语言的动态特性的具体表现。变量的寻址方式也是一个动态的过程。程序经编译后,获得了变量的相对于程序代码段(CS)或数据段(DS)的偏移量信息,在程序执行时,操作系统首先将程序装载到内存中足够大小的某个单元中,这个单元的首地址加上变量的偏移量就形成了变量的存储单元地址。此外,面向对象语言中运行时多态性是一个重要的动态特性。从这个意义上说,早期Fortran语言、C、Pascal都具备一定程度的动态特性,但比较弱,而Smalltalk、Lisp、Python等语言在运行时可以改变变量的类型甚至时自身的程序结构,我们就认为这样的语言具有更强的动态特性。
不同的语言具有不同程度的动态特性。纵观程序设计语言的发展史,如果将Fortran和Lisp语言看作是语言在动态特性上的两个极端的话,那么现代大多数语言都是介于二者之间的折中,综合考虑了开发效率和执行效率以及其他众多因素。各种语言也在相互借鉴,不断演变。下图是以Fortran和Lisp为两头,比较了几种语言的动态性程度:
在上图中,从左到右语言的动态特性逐渐加强。处于最左端的Fortran语言不支持堆栈,所有的变量和子程序都是在编译时分配好内存的,不能进行动态内存分配,因而不能进行函数递归调用,许多问题的解决方式受到极大的限制。这主要是由早期Fortran语言的设计目标决定的,早期Fortran语言主要是为了解决科学和工程中的计算问题,优先考虑的是语言的执行效率。虽然Fortran语言被无数次认定为过时,然而它仍然继续发展着。 Fortran90大大扩展了传统Fortran的功能,增加了现代高级语言的数据和控制特征,允许内存的动态分配,使得它具有和C和Pascal语言相当的能力。
比Fortran动态性更强的C/C++语言提供了指针,支持堆栈,提供了malloc/new和free/delete操作,运行时可以动态分配和释放内存,可以比较灵活地动态生成对象并分配存储空间。此外,C++语言中的RTTI(RunTime Type Identification)机制可以在只有指向某个对象的基类指针的情况下,根据驻留在虚函数表中的类型信息,在运行时确定该对象的准确类型。然而,C/C++程序中的变量类型仍然需要在编译时确定下来,大部分类型检查也是在编译时完成的,执行前完成了大部分的绑定工作。
现在流行的 Java和C#语言的动态特性之所以要比C/C++强,是因为Java/C#提供了更强的反射Reflection机制,可以在运行时通过 Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现的interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤醒methods。如在C#中,可以通过System.Type类在运行时获取任何给定数据的类型信息,包括构造函数、属性、方法和事件,也可以唤醒(Invoke)相关的方法(具体的使用可参考Microsoft出版的《Inside C#》的“Querying Metadata with Reflection”一章)。
而Smalltalk以右的语言,变量的类型绑定和类型检查都是在运行时进行的。Perl、Python和Ruby语言可以在运行时修改类的结构或定义,变量的类型可以按需改变,编写程序时无须声明变量类型。Lisp语言甚至可以在运行时动态地改变自身的代码, Lisp语言对程序代码和数据一视同仁,都看作是存储在内存中的对象,这样数据结构既可作为程序执行,程序也可作为数据进行修改。
上面介绍的几种语言都具备一定程度的动态特性,那么应该如何定义动态语言呢?目前在许多开发社群中,提及“动态语言”,一种普遍的观点是认为“动态语言是指能够在运行时改变程序结构和变量类型的语言”,有时也称作“动态类型语言”(Dynamic Type Language),本文采用的就是这种观点,在下文中的动态语言都是指具有这种性质的语言,与之相对的概念“静态语言”指的是在编译阶段确定变量或数据对象类型的语言。按照这种说法,上图中Smalltalk/Perl以左的为静态语言,以右的语言都为动态语言。这里需要说明的是,许多技术文章和语言研究中都涉及到“脚本语言”的概念,实际上脚本语言就是动态语言,因为脚本语言也具备在运行时动态改变类型的性质,符合动态语言的定义,本文不区分这两个概念,一律使用“动态语言”,强调该类语言的最明显的特征。另外,有的人也将“静态语言”称之为“系统程序设计语言”,因为大多数静态语言都是系统程序设计语言,这两个概念只是从不同的角度描述了同一类语言,各自的侧重点不同,“静态语言”的“静态”是基于绑定时间的,强调了语言中大部分绑定关系是发生在运行前,而“系统程序设计语言”强调语言所能完成的功能是侧重于编写系统程序。
3.2 动态语言的特点
近几十年来,传统的软件开发模式和工具、编程语言已难以适应软件需求的变化,软件的混合式开发逐渐增加。随着动态语言的兴起,人们的编程方式和观念发生了转变。在诸如C/C++、 Java、C#之类的静态语言中,语法规则迫使程序员在使用一个变量前首先要声明它的类型,变量的类型一旦确定下来,那么在整个程序中都不能改变,只能是一种类型。而在象Smalltalk、Python、Perl、Tcl、Ruby、Visual Basic和Unix Shells的动态语言中,无类型化定义使程序员从庞大的类型系统解脱出来,变量不经声明就可以直接使用,变量的类型可以按需改变,编写程序变得轻松自由。本节主要介绍动态语言的特点和编程方式,以及与静态语言的主要区别和联系。
动态语言,顾名思义,最显著的特点就是在于它的“动态性”,即运行时可以按需改变程序结构和变量类型、函数的定义等,这也是与静态语言的根本区别。除此之外,动态语言还有许多特点,这些特点深受开发人员的喜爱,使得动态语言渐入人心。
3.2.1 动态类型
类型系统影响了语言的诸多特性。在静态语言中,声明的目的是为了告诉编译器所使用的变量“是什么”,使类型检查可以在编译阶段静态地进行,尽量减少执行时的类型检查,提高执行效率,但缺乏灵活性。而在动态语言中,没有声明语句,赋值语句将名称绑定在数据对象上,如果名称赋予数据对象一种类型,稍后也可以给对象赋予另外一种类型。变量被设计成无类型的,变量的类型可以按需改变,同一个变量既可作为整型的,也可作为字符串,还可用来定义函数。
我们知道,程序中定义的操作一般需要特定类型的参数作为操作的输入,操作只有在接收到类型正确的参数时才能正确无误的执行,最典型的实例就是函数的定义,函数的原型包括函数的参数列表和返回值类型,参数列表提供输入参数的全部信息,执行函数前首先要进行参数类型的检查。在动态语言中,变量是无类型的,那么如何保证所执行的操作是否接收到类型正确的参数呢?在运行时进行动态类型检查机制解决了类型安全这一问题。动态类型检查通过在每个数据对象中保存一个类型标签表明该数据对象的类型,比如在表达式C=A+B中,A和B的类型在程序运行时确定,也可以在运行时改变,所以每次执行 + 操作时都要根据类型标签对A和B的类型进行检查,只有在类型正确的情况下才能执行,否则发出错误信号。操作正确执行后,确定了变量C的类型,并记下C的类型标签以备随后可能的操作进行类型检查。
显然,动态类型检查不能静态地检测到程序代码中类型不匹配的错误,并不意味着动态类型容易在程序中引入类型安全的错误。事实上,在诸如C++、Java之类的静态类型语言中虽然能够在编译时尽可能多的检测到程序中类型失配的错误,但类型仅仅是数据的一小部分信息,类型正确并不能保证程序中不存在其他的错误。在大规模的程序中,要为类型上编写大量的语句,这就使得程序员专注于程序中的类型正确性而容易忽视程序其他部分的正确性。有些问题用静态类型很难实现,例如对于一个不支持泛型编程的语言来说实现一个可以支持多种类型的容器类就比较困难,假如编写了一个可以存放objects的容器类,在具体应用时要通过向上或向上转换成所需的类型,而这种转换往往是不安全的。而动态类型语言不需要这种转换,它所实现的容器类完全是泛型的,在语言一级就提供了良好的支持[3]。裘宗燕教授则认为“很多人之所以在最初使用具有丰富动态特征的语言编程时容易犯错误,主要是因为他们习惯了诸如C、Pascal等语言,不了解用这些动态特征丰富的语言中编程的一些必要的风格和习惯。如果熟悉了这些风格和习惯,犯错误这件事情同样是可能很好地避免的。使用弱类型的语言同样可以开发很好的系统,而且实际上已经开发了很多很好的系统。”[6]。
动态类型检查的主要优点在于程序设计的灵活性,不需要声明语句,一个变量名绑定的数据对象的类型可以在程序执行时按需改变,使程序员从数据类型摆脱出来,同时也可以编写更少的程序代码行完成同样的功能。动态类型是动态语言的最显著的优点,但也是动态语言的弱点根源所在。运行时进行的类型检查也存在几点重大不足:
a. 程序难以调试。因为动态类型检查只在程序运行到某一条操作时才对其进行类型检查,而ü 从来不ü 检查没有被执行的执行路径上的操作。在软件测试时是不ü 能遍历程序中所有的执行路径,ü 这样没有被执行的路径仍有可能存在bugs。这一点可能是动态语言致命的缺点,它导致了动态语言对开发大型软件项目支持力度不够。
b. 保存大量的类型信息。运行时需要相当大的额外存储空间。
c. 执行效率低。动态类型检查要靠软件模拟实现,主要是在运行时完成的,所以在执行速度上降低了不少。
静态类型和动态类型都有各自的优点和缺点,不能简单地认为静态类型比动态类型要好或者动态类型比静态类型要好[3],关键要看具体应用的场合。
3.2.2 开发效率高
在动态语言社区里,值得开发人员津津乐道的是动态语言具有比静态语言高数倍的开发效率。虽然静态语言在执行效率上比动态语言略胜一筹,但在开发效率上只能甘拜下风。正如前文所提到,程序设计语言逐步从面向机器到面向人过渡,以牺牲运行效率来换取开发效率。如今,计算机硬件性能的大大提升,我们能够承受起动态语言在执行效率上的损失,实际上,只有很少的应用才需要尽可能的利用机器的性能。目前,普遍认为Python的开发效率要比Java高出5-10倍。那么动态语言为什么会在开发效率方面具有天独厚的优势?下面就从几个方面进行说明:
a. 易学易用,编程人员可快速上手
Python、 Perl、Ruby等动态语言具有简洁的语法规则,交互式的编程环境,比C++、Java等静态语言更容易学习和掌握。比如Python语言,语言的语法结构、控制结构和C/C++十分类似,对于一个熟悉C、C++语言的编程人员来说,花3-5个小时完全可以掌握Python的语法特性。软件的复杂性在于整合了千丝万缕的关系,交互式环境把程序分解成一块块,对于不熟悉的语言特性,可以方便输入交互式命令进行测试。动态语言不需要声明语句的编程方式使得动态语言更加接近于自然语言,简单明了,易学易读,许多优秀的语言特性都是语言大师深思熟虑之后的产物。对于一个软件项目来说,项目经理更愿意选择一门易于上手又同样可以完成任务的编程语言作为软件的开发语言,一则可以减少员工的学习成本,二则有利于缩短项目完工的时间。
动态语言的易学易用性最终导致了编程群体的改变。二十年前大多数编程者是大型项目的专业编程人员.那个时代的编程人员需要花几个月的时间掌握一门语言和它的编程环境,系统程序设计语言(静态语言)就是为这些人设计的.然而,自从个人电脑出现以后,越来越多的非专业编程者加入到编程者的行列.对这些人来说,编程不是他们的主要工作,而只是他们偶尔用来帮助他们工作的工具。偶尔编程的例子是编写简单的数据库查询或者是电子数据表的宏.非专业编程者不希望花几个月的时间学习一门专为系统程序而设计的静态语言,但他们可以花几个小时的时间学到足够的动态语言知识来写出有用的脚本代码。由于动态语言有简单的句法并且省略了诸如对象和线程的复杂特性,因而它比静态语言要更容易学习和掌握。例如,非专业编程人员很少会选择Visual C++,而大部分会用Visual Basic编写有用的应用程序[1]。
b. 内置丰富的数据结构和操作
实际上,动态语言是建立系统程序设计语言之上的语言,大多数动态语言解释器的内核都是用某种静态语言实现的,Python是用C实现的,JVM上的Jython是用纯Java实现的,.Net CLR上的IronPython是用C# 实现的。动态语言将灵活性作为其设计的目标,优先考虑了语言的灵活性。因此,动态语言在语言层次上集成了许多操作方便、性能良好、高度抽象的数据类型,为编程人员提供了高效的数据结构实现,有助于提高开发效率。以Python语言为例。Python语言提供了强大的数学运算,可以处理超大的整数和浮点数,支持复数的基本运算。Python内置了功能强大、使用方便的数组类型,程序员不需要预先声明使用数组的大小就可以直接使用数组,也不必担心数组大小不够,这些解释器会自动根据具体地操作动态地分配数组的存储空间,不象C/C++中使用数组首先必须声明数组的大小,这就使得很多人在使用数组时不知道究竟应将大小设置成多少,大了觉得浪费,小了又担心不够,想改成指针方式动态分配又有可能给程序带来新的问题。对数组类型还有一系列灵活的操作,比如可以在数组任何位置插入元素,可以对数组分片,可以将数组的某一片作为参数传递给某个函数而并不需要将数组从头到尾的都传递。在Python的具体应用中使用可能最广当属字典类型(Dictionary Type)。字典类型是Python内置的高效强大的数据类型,是一种类似于关联表和哈希表的结构,Python的实现者花费了大量的时间对字典类型做了优化,以尽可能保证语言的高效性。Python内部实现也使用了大量的字典类型。如果没有提供这些内置的数据类型,开发人员可能需要独立开发出支持所需的数据类型的库程序,而开发这样库不但费时,而且在性能优化和接口通用性方面欠佳。正是因为动态语言内置了丰富的数据类型,节省了开发人员独立实现这些数据类型的时间,从而提高了程序的开发效率[4]。
c. 无类型化,用更少的代码可完成同样的工作
动态语言的动态类型使得编写程序时不需要声明变量的类型,变量无类型化省去了程序代码中大量的编译器编译时所需的类型信息语句,使程序看上去简洁明了。
比如在Tcl语言中,如命令:
button .b -text Hello! -font {Times 16} -command {puts hello}
创建了一个新的按钮来显示16点Times字体,当用户敲击控制键时显示一段小信息。它把六种不同事件类型混合成一条语句:一个命令名(button)、一个按钮控制(.b)、所有权名字(-text, -font, 和-command),简单字符串(Hello! 和hello),包含字样名(Times)及字点大小(16)的字体名(Times 16)和Tcl脚本(puts hello)。Tcl统一用字符串表示了这些类型。
上述例子在Java中要调用两个方法完成,需要7行代码。用C++和微软基本类(MFC)需要三个过程,25行代码,在微软基本类中仅仅设置字体就需要几行代码:
CFont *fontPtr = new CFont();
fontPtr->CreateFont(16, 0, 0,0,700, 0, 0, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH|FF_DONTCARE, Times New Roman);
buttonPtr->SetFont(fontPtr);
大部分代码是由静态类型造成的。为了设置按钮字体,必须运用SetFont方法,但这个方法必须通过指针传给CFont对象,这反过来需要声明和初始化一个新的对象.为了初始化CFont对象必须唤醒它的CreateFont 方法,但CreateFont有一个需要14个特殊化引数的固定接口。在Tcl中字体(Times铅字,16点)的基本特征不用声明或转换就可以立即使用。另外,Tcl允许在创建按钮的命令中直接包含按钮行为,而C++和Java中需要把它放在单独声明的方法中。
高级程序设计语言的高级性在于它的每一条代码可以完成更多的工作,完成同样的工作,使用的代码愈少,表明语言的等级愈高。每行汇编代码平均可以翻译成1-3条机器指令,与此相比,每行 C/C++代码平均翻译成5条机器指令,而对于Python、Perl、Tcl这样的动态语言,每行代码可翻译成100-1000行机器指令。汇编程序中,基本上是没有类型,所有的数据看起来都是一样,机器的每个细节都暴露在代码中,程序员必须显式地管理寄存器和内存分配、栈调用等等,所有的操作都涉及到最底层的设备操作,对程序员的专业素质要求高,用汇编语言开发程序效率低下,因此难以编写和维护大型程序。到高级语言出现后,编译器隐藏了机器的底层细节,能够自动管理存储分配、目标代码生成和优化、过程调用顺序等操作,有了抽象数据类型,将程序员从面向机器的复杂中解脱出来,提高了程序的开发效率。
动态语言把许多工作都交给解释器去完成,程序员专注于自己需要解决的问题,建立问题解决方案的逻辑就够了,因此更接近于自然语言。比起静态语言,动态语言的一条语句可以完成更多的功能。从这个意义上说,动态语言是一种更高级的语言。因此,动态语言具有更高的开发效率。
3.2.3 支持动态重构
随着软件应用面临的问题多样化和复杂化,有些应用领域中逐渐出现这样一类需求:要求软件在运行时还能够改变,即动态重构。比如在一个银行系统中,要求软件一旦运行,就不能停下来,否则会对银行造成灾难性的损失,那么这样的系统中如果在运行时发现某个错误怎样修正呢?显然,用静态语言很难实现,因为静态语言在运行时不能修改自身代码,用静态语言实现的系统执行的是经过翻译处理后的可执行代码,要修正软件必须要经过修改源代码->编译->连接 ->生成可执行程序的步骤。而动态语言比较容易实现,动态语言是解释执行的,运行时还能够修改自身的代码。国外有人用Smalltalk实现了这样一个分布式的银行系统,银行系统留有一个接口,开发好的系统发现bug后,维护人员可以通过该接口远程登录后,可以在系统不停止运行的情况下把bug消除掉。
3.2.4 胶水语言(Glue Language)
动态语言一个广泛的应用就是作为“胶水语言”胶合用静态语言编写的组件,从而整合成一个应用程序,这种应用称之为混合式应用。RedHat Linux的安装程序就是通过Python胶合各个组件模块实现的。静态语言开发的程序运行速度快,但开发周期较长,而动态语言灵活简洁,开发效率可提高 5-10倍,但程序运行慢,混合式应用合理结合各自的优点,充分利用不同语言的各自优势,取长补短,可以快速高效地构建应用程序并具有相当的性能。[8]
这里介绍动态语言具有静态语言所没有的优点并不是意味着动态语言要替代或推翻静态语言,而是作为静态语言有力的补充。当然,动态语言不能回避的就是它与生俱来的缺陷,比如执行效率低下、对大型软件开发的支持力度不够等等,使得动态语言难以成为主流的开发语言。现代软件面临的问题越来越复杂,软件工程领域中没有“银弹”可用,同样,也没有一种语言可以成为软件开发的“万金油”,可以很好地解决各类问题,每种语言都各有所长,也各有所短。动态语言和静态语言相互补充,相得益彰,扬长避短,才能充分发挥不同语言的优势,在软件开发中做到“动中有静,静中有动,动静结合,各司其职”。
3.3 动态语言的发展
近几年来,动态语言的发展势头良好,受到越来越多的关注,开发社区显得十分活跃。尤其是Internet的发展,为动态语言提供一个良好的展现个性的活动舞台。Perl因为编写CGI而变得流行,VBScript和JavaScript因为能编写网页而在互联网上广泛使用。
目前,动态语言已经进军当前主流的Java VM/.Net平台。Jython是Python语言在Java VM上的完全实现,是用纯Java语言编写的,可以轻松的访问所有的Java类库。Groovy也是Java VM上一款非常流行的动态语言,并作为JSR草案正式提交给标准化组织审议。IronPython是.Net平台上Python的高效实现。
动态语言的应用已经深入到系统脚本、服务器程序、游戏开发、数据库引擎和CAD系统的开发中,我们有理由相信,随着Internet的发展趋势和混合式应用的扩展,计算机硬件的快速发展,动态语言将会展现蓬勃的生命力,与静态语言一同构建强劲的编程环境,在软件开发中扮演越来越重要的角色。
发表评论
-
Mixin and Trait
2013-11-24 20:23 2831对于Mixin(混合)、Trait ... -
pygraphviz 在windows 7/ python 2.7 下编译安装
2013-10-17 21:59 3396pygraphviz 在windows 7/ python 2 ... -
(转)Method Resolution Order (MRO) in new style Python classes
2013-09-20 22:08 1034Stack Overflow link: http://st ... -
python实现的单子模式(附解释)
2013-07-13 21:06 2223一、通过metaclass来实现 class Sing ... -
SQL注入
2013-04-25 14:24 0http://www.ilovn.com/topic/wing ... -
Wing IDE 4.1 破解方法
2013-04-25 14:23 1869Wing IDE 4.1版本破解方法: 1、去官网下载最新版 ... -
【转】探索Python下的property, classmethod, staticmethod的实现
2013-04-08 21:22 1362转:http://marlonyao.iteye.com/bl ... -
python bytes and string 编码
2012-12-31 15:58 24401http://linchunai1212.blog.163.c ... -
Python subprocess
2012-12-31 15:07 3487Python标准库06 子进程 (subprocess包) 作 ... -
Python作用域和参数
2012-10-24 21:48 1143转(http://blog.lzp729.com/code-l ... -
Python作用域(转)
2012-10-24 21:45 1491python 变量作用域 分类: Python 2011-07 ...
相关推荐
* 跨平台性:Java语言的编译器将源代码编译成字节码,可以在任何支持Java虚拟机(JVM)的平台上运行,实现了跨平台性。 * 面向对象:Java语言是一种纯面向对象的编程语言,支持封装、继承和多态等面向对象特性。 * ...
本文将深入探讨如何在WPF项目中实现多国语言支持,并且讲解如何实现在运行时动态切换应用的语言环境。 首先,我们需要创建语言资源文件。这些文件通常以.resx扩展名存储,每种语言对应一个文件。例如,对于英文和...
8. 平台无关性:Python语言可以运行在多个平台上,包括Windows、Linux、Mac OS等,具有很好的平台无关性。 9. 开源免费:Python语言是一种开源免费的编程语言,任何人都可以自由获取、使用和修改它。 10. 社区活跃:...
此外,考虑到不同设备和Android版本的兼容性,开发过程中需要测试多种设备和Android版本,确保语言切换功能在各种环境下都能正常工作。同时,为了提供更好的用户体验,应避免频繁地切换语言,因为这可能导致界面闪烁...
* 动态性 * 生产力高 * 社区活跃 然而,Ruby也有一些缺点: * 性能较低 * 内存占用较高 * 依赖管理复杂 * 文档不足 Ruby语言的未来发展 随着时间的推移,Ruby的应用范围和功能不断扩展和改进。Ruby社区也在不断...
"Java语言程序设计第三版-习题答案.pdf" Java语言程序设计第三版-习题答案.pdf是Java语言程序设计的习题答案,涵盖了Java语言的基础知识、语法机制、平台架构、特征等方面。下面是该资源的知识点摘要: Java技术...
这些资源可以在程序运行时根据用户选择的语言动态加载。 2. **动态语言切换**:通过 TsiLang 控件,开发者可以轻松实现应用程序的动态语言切换。用户可以在程序运行时自由选择界面语言,无需重启程序。 3. **事件...
QML的强大之处在于其灵活性和可扩展性,它可以方便地与C++代码交互,实现复杂的逻辑和数据绑定。 1. **资源文件和i18n**:在实现多语言支持时,开发者通常会创建一个资源文件夹,包含不同语言的翻译文件。这些文件...
而切片则以其动态大小和灵活性,在处理不确定数量的数据时更为便捷。理解它们之间的差异以及如何根据具体需求选择合适的数据结构对于Go语言开发者来说至关重要。 以上内容不仅限于理论知识,还包括了实际操作的代码...
根据提供的标题“Python源码剖析 深度探索动态语言核心技术.pdf”以及描述中的相同信息,我们可以推断出这份文档的主要内容是关于Python语言的核心技术深入解析,特别是针对Python作为一门动态语言的特点进行探讨。...
总的来说,使用汇编语言实现动态链表是一个挑战性的任务,它要求开发者深入理解内存管理、指针操作和数据结构。这样的实践有助于提升对计算机底层运作的理解,对系统级编程和优化有着重要的意义。
标题中的“动态函数式语言精髓”指的是编程领域中关于动态函数式编程的一种深入探讨。动态函数式编程是一种编程范式,它结合了动态类型和函数式编程的特点,强调代码的简洁性、表达性和可读性。这类语言通常包括Lisp...
《脚本语言与动态网页设计》是由张景峰主编,由中国水利水电出版社在2004年出版的一部教材,旨在深入浅出地讲解如何利用脚本语言来构建交互式的动态网页。动态网页设计是互联网技术的重要组成部分,它使得网站能够...
《程序语言的奥妙:算法解读》是一本深入探讨编程语言与算法紧密关系的专业书籍。在计算机科学领域,算法是解决问题的关键,而编程语言则是实现这些算法的工具。本资料详细解析了各种常见编程语言如何有效地支持和...
本篇文章将深入探讨在QT中实现多国语言动态切换的常见方法。 首先,我们需要了解QT中的QTranslator类,它是实现语言切换的关键。QTranslator负责加载和应用翻译文件(.qm),这些文件由QT Linguist工具生成,包含了...
【标题】"华农Linux系统及程序设计实验全部代码+4种动态网页语言完成综合性实验"揭示了这个压缩包中的核心内容,它包含了与Linux操作系统和程序设计相关的实验代码,以及使用四种不同的动态网页语言(如PHP、Python...
本示例的优势在于它的轻量级和直观性,能够快速实现基本的多语言切换功能,而不会带来额外的复杂性。 总结一下,"html多语言切换demo"通过JavaScript和HTML的`lang`属性,提供了一个简单易用的多语言解决方案。它...