动态语言

动态编程语言高级编程语言的一个类别,在计算机科学领域已被广泛应用。它是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。动态语言目前非常具有活力。众所周知的ECMAScriptJavaScript)便是一个动态语言,除此之外如PHPRubyPython等也都属于动态语言,而CC++Java等语言则不属于动态语言。

大部分动态语言都使用动态类型,但也有些不是。

实现编辑

动态语言可能包含的特征有:eval函数、对象运行时间改变、反射巨集

对象运行时间改变编辑

在动态语言中类型或对象系统典型的是可以改变的。这意味着可以从运行时间定义或基于现存类型或对象的mixin来生成新对象。这还可以用来称谓变更继承或类型树,从而改变了现存类型系统表现的方式(特别是关于方法调用)。

Eval编辑

一些动态语言提供eval函数。这个函数接受包含这个语言写的代码的一个字符串形式参数并执行它。如果这个代码表示了一个表达式,则返回它的结果值。Erik Meijer英语Erik Meijer (computer scientist)和Peter Drayton声称可以:“使用eval作为高阶函数的穷人替代品”[1]

反射编辑

反射常见于很多动态语言中,典型的涉及到类型和泛化或类型多态数据的元数据的内省。但是它还可以包括将程序代码作为数据的完全求值和修改,比如Lisp的分析S-表达式的那种特征。

巨集编辑

有限数目的动态语言提供一种叫做巨集的特征,用来组合代码内省(检查类、函数和关键字,知道它们是什么、干什么、知道什么)和eval。多数现在的程序员是从CC++中知道这个术语的,在这里它们是建造入语言的小子集中的一种静态特征,只有在程序的文本上进行字符串替换的能力。在动态语言中,它们提供对编译器的内部工作的访问,和对解释器、虚拟机或运行时系统的完全访问,允许定义可以优化代码或修改语言的语法或文法的类似语言的构造。

例子代码编辑

下列代码使用Common Lisp和它的Common Lisp对象系统(CLOS)展示语言的动态特征。

运行时间的代码计算和后期绑定编辑

这个例子展示函数可以在运行时间从源代码进行修改:

; 源代码作为数据存储在一个变量中
CL-USER > (defparameter *best-guess-formula* '(lambda (x) (* x x 2.5)))
*BEST-GUESS-FORMULA*

; 从源代码创建一个函数并在运行时间执行,这个函数可获得于best-guess名下
CL-USER >  (compile 'best-guess *best-guess-formula*)
#<Function 15 40600152F4>

; 这个函数可以被调用
CL-USER > (best-guess 10.3)
265.225

; 源代码可以在运行时间改进
CL-USER > (setf *best-guess-formula* `(lambda (x) ,(list 'sqrt (third *best-guess-formula*))))
(LAMBDA (X) (SQRT (* X X 2.5)))

; 这个函数的新版本被编译
CL-USER > (compile 'best-guess *best-guess-formula*)
#<Function 16 406000085C>

; 下次调用将调用新版本,这是后期绑定的特征
CL-USER > (best-guess 10.3)
16.28573

对象的运行时间改变编辑

这个例子展示,现存的实例在它的类变更时可以被变更来包括一个新槽,还有现存的方法可以被替代为新版本:

; person类,person有一个name
CL-USER > (defclass person () ((name :initarg :name)))
#<STANDARD-CLASS PERSON 4020081FB3>

; 给类person的对象的一个定制的打印方法
CL-USER > (defmethod print-object ((p person) stream)
            (print-unreadable-object (p stream :type t)
              (format stream "~a" (slot-value p 'name))))
#<STANDARD-METHOD PRINT-OBJECT NIL (PERSON T) 4020066E5B>

; 一个例子person实例
CL-USER > (setf *person-1* (make-instance 'person :name "Eva Luator"))
#<PERSON Eva Luator>

; 类person得到第二个槽,它从而拥有槽name和age
CL-USER > (defclass person () ((name :initarg :name) (age :initarg :age :initform :unknown)))
#<STANDARD-CLASS PERSON 4220333E23>

; 更新打印这个对象的方法
CL-USER > (defmethod print-object ((p person) stream)
            (print-unreadable-object (p stream :type t)
              (format stream "~a age: ~" (slot-value p 'name) (slot-value p 'age))))
#<STANDARD-METHOD PRINT-OBJECT NIL (PERSON T) 402022ADE3>

; 现存的对象已经被变更了,它有了增加的槽和新的print方法
CL-USER > *person-1*
#<PERSON Eva Luator age: UNKNOWN>

; 可以设置实例的新age槽
CL-USER > (setf (slot-value *person-1* 'age) 25)
25

; 这个对象已经被更新
CL-USER > *person-1*
#<PERSON Eva Luator age: 25>

基于实例的类的运行时间代码组装编辑

在下列例子中,类person得到一个新超类。print方法得到重新定义,它组装了多个方法成为有效(effective)方法。基于实际参数的类和运行时间可获得和适用的方法,有效方法得以组装:

; 类person
CL-USER > (defclass person () ((name :initarg :name)))
#<STANDARD-CLASS PERSON 4220333E23>

; person只打印它的name
CL-USER > (defmethod print-object ((p person) stream)
            (print-unreadable-object (p stream :type t)
              (format stream "~a" (slot-value p 'name))))
#<STANDARD-METHOD PRINT-OBJECT NIL (PERSON T) 40200605AB>

; 一个person实例
CL-USER > (defparameter *person-1* (make-instance 'person :name "Eva Luator"))
*PERSON-1*

; 显示这个person实例
CL-USER > *person-1*
#<PERSON Eva Luator>

; 现在重新定义print方法为可扩展的
; :around方法创建print方法的上下文,并且它调用call-next-method
CL-USER > (defmethod print-object :around ((p person) stream)
            (print-unreadable-object (p stream :type t)
              (call-next-method)))
#<STANDARD-METHOD PRINT-OBJECT (:AROUND) (PERSON T) 4020263743>

; 主要方法打印name
CL-USER > (defmethod print-object ((p person) stream)
            (format stream "~a" (slot-value p 'name)))
#<STANDARD-METHOD PRINT-OBJECT NIL (PERSON T) 40202646BB>

; 新类id-mixin提供了一个id
CL-USER > (defclass id-mixin () ((id :initarg :id)))
#<STANDARD-CLASS ID-MIXIN 422034A7AB>

; :after方法只打印id槽的值
CL-USER > (defmethod print-object :after ((object id-mixin) stream)
          (format stream " ID: ~a" (slot-value object 'id)))
#<STANDARD-METHOD PRINT-OBJECT (:AFTER) (ID-MIXIN T) 4020278E33>

; 现在重新定义类person来包括混入的id-mixin
CL-USER 241 > (defclass person (id-mixin) ((name :initarg :name)))
#<STANDARD-CLASS PERSON 4220333E23>

; 现存实例*person-1*现在有了一个新槽并设置它为42
CL-USER 242 > (setf (slot-value *person-1* 'id) 42)
42

; 再次显示这个对象,print-object函数现在有一个有效方法,它调用三个方法::around方法、主要方法和:after方法
CL-USER 243 > *person-1*
#<PERSON Eva Luator ID: 42>

语言编辑

参见编辑

引用编辑

  1. ^ Meijer, Erik and Peter Drayton, Static Typing Where Possible, Dynamic Typing When Needed: The End of the Cold War Between Programming Languages, Microsoft Corporation, 2005 
  2. ^ Archived copy. [2014-03-02]. (原始内容存档于2014-03-02). 

延伸阅读编辑

外部链接编辑