動態語言

本頁使用了標題或全文手工轉換
維基百科,自由的百科全書

動態程式語言高階程式語言的一個類別,在電腦科學領域已被廣泛應用。它是一類在執行時可以改變其結構的語言:例如新的函式、對象、甚至代碼可以被引進,已有的函式可以被刪除或是其他結構上的變化。動態語言目前非常具有活力。眾所周知的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)展示語言的動態特徵,具體採用的實現是SBCL

代碼在執行時間算得和後期繫結[編輯]

這個例子展示函式可以在執行時間從原始碼進行修改:

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

; 从源代码创建一个函数并在运行时间执行,这个函数可获得于best-guess名下
* (compile 'best-guess *best-guess-formula*)
BEST-GUESS
NIL
NIL

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

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

; 这个函数的新版本被编译
* (compile 'best-guess *best-guess-formula*)
BEST-GUESS
NIL
NIL

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

對象執行時間改變[編輯]

這個例子展示,現存的實例在它的類變更時可以被變更來包括一個新槽,還有現存的方法可以被替代為新版本:

; person类,person有一个name
* (defclass person () ((name :initarg :name)))
#<STANDARD-CLASS COMMON-LISP-USER::PERSON>

; 给类person的对象的一个定制的打印方法
* (defmethod print-object ((p person) stream)
    (print-unreadable-object (p stream :type t)
      (format stream "~a" (slot-value p 'name))))
#<STANDARD-METHOD COMMON-LISP:PRINT-OBJECT (PERSON T) {10019FFF13}>

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

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

; 更新打印这个对象的方法
* (defmethod print-object ((p person) stream)
    (print-unreadable-object (p stream :type t)
      (format stream "~a age: ~d" (slot-value p 'name) (slot-value p 'age))))
WARNING:
   redefining PRINT-OBJECT (#<STANDARD-CLASS COMMON-LISP-USER::PERSON>
                            #<SB-PCL:SYSTEM-CLASS COMMON-LISP:T>) in DEFMETHOD
#<STANDARD-METHOD COMMON-LISP:PRINT-OBJECT (PERSON T) {1001AB5793}>

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

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

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

基於實例的類的代碼在執行時間組裝[編輯]

在下列例子中,類person得到一個新超類。print方法得到重新定義,它組裝了多個方法成為有效(effective)方法。基於實際參數的類和執行時間可獲得和適用的方法,有效方法得以組裝:

; 类person
* (defclass person () ((name :initarg :name)))
#<STANDARD-CLASS COMMON-LISP-USER::PERSON>

; person只打印它的name
* (defmethod print-object ((p person) stream)
    (print-unreadable-object (p stream :type t)
      (format stream "~a" (slot-value p 'name))))
#<STANDARD-METHOD COMMON-LISP:PRINT-OBJECT (PERSON T) {10019C7F13}>

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

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

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

; 主要方法打印name
* (defmethod print-object ((p person) stream)
    (format stream "~a" (slot-value p 'name)))
WARNING:
   redefining PRINT-OBJECT (#<STANDARD-CLASS COMMON-LISP-USER::PERSON>
                            #<SB-PCL:SYSTEM-CLASS COMMON-LISP:T>) in DEFMETHOD
#<STANDARD-METHOD COMMON-LISP:PRINT-OBJECT (PERSON T) {1001B929A3}>

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

; :after方法只打印id槽的值
* (defmethod print-object :after ((object id-mixin) stream)
    (format stream " ID: ~a" (slot-value object 'id)))
#<STANDARD-METHOD COMMON-LISP:PRINT-OBJECT :AFTER (ID-MIXIN T) {1001C96D03}>

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

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

; 再次显示这个对象,print-object函数现在有一个有效方法,它调用三个方法::around方法、主要方法和:after方法
* *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). 

延伸閱讀[編輯]

外部連結[編輯]