在計算機編程中,同像性(homoiconicity來自希臘語單詞,homo-意為相同,icon含義表像),是某些程式語言的特殊屬性,這意味着用此語言書寫的程序,可用使用這個語言將其作為數據來操縱,因此只要閱讀程序自身,就能推論出來這個程序的內部表示。該屬性經常被歸結成,這個語言將「代碼當作資料」。

簡介

編輯

在同像性編程語言中,程序的主要表示方式,也是屬於這個語言自身的原始類型的一種資料結構。這使得在這種語言中的元編程,比在沒有這個屬性的語言中要更加容易:在這種語言中的反射(在運行時檢查程序的實體),取決於單一的、同質的結構,而且它不必去處理以複雜語法形式出現的其它一些結構。同像性語言典型的包括對語法宏的完全支持,這允許編程者以簡明方式來表達程序變換。

Lisp編程語言,是具有同像屬性的典型範例,它設計得易於進行列表操縱,而且其結構用具有嵌套列表形式的S-表達式來給出,它可以由其他LISP代碼來操縱[1]。這類語言的其他例子有Clojure(一種現代流行的LISP方言),RebolRefal英語Refal,以及最近的Julia等編程語言。

歷史

編輯

同像性一詞的原始來源,是論文《編譯器語言的巨集指令擴展》[2]。其依據是早期具影響力的論文《TRAC英語TRAC (programming language)文本處理語言》[3]

TRAC的主要設計目標之一,是其輸入腳本(用戶所輸入),應該同一於指示TRAC處理器內部動作的文本。換句話說,TRAC過程應該是以字串形式儲存於記憶體中,正如同用戶在鍵盤上鍵入的那樣。如果TRAC過程本身演化出新的過程,這些新過程也應該在同一個腳本中陳述出來。TRAC處理器在其動作中,將此腳本解釋為它的程序。換句話說,TRAC翻譯器程序(處理器),將這個計算機有成效地轉換為,具有新程序語言即TRAC語言的新計算機。在任何時候,程序或過程資訊都應當能夠,以同於TRAC處理器在執行期間作用於其上的形式來顯示出來。我們期望內部的字符代碼表示,同一於或非常相似於,外部的代碼表示。在當前的TRAC實作中,內部字符表示基於ASCII,因為TRAC過程和文本,在處理器內部和外部,都具有相同的表示,所以術語同像性(homoiconic)一詞是適用的,homo涵義相同,icon義為表像。

[...]

跟從沃倫·麥卡洛克的提議,依據查爾斯·桑德斯·皮爾士的術語,參見道格拉斯·麥克羅伊的「編譯器語言的巨集指令擴展」,ACM通訊,頁214-220; 1960年4月。

艾倫·凱在他1969年的博士論文中,使用並可能由此推廣了同像性這個術語[4]

所有先前的系統中,顯著的一組例外是Interactive LISP[...]和TRAC。兩者都是面向功能性的(一為列表,另一為字符串),都用一種語言與用戶交談,並且都具有 「同像性」,因為它們內部和外部表示本質上相同。它們都具有動態創建新函數的能力,然後可隨着用戶的喜好而精工細作。它們唯一最大的缺點是,以它們寫出的程序看起來就像,蘇美爾人把布爾那·布里亞什國王的信寫成巴比倫楔形文![...]

用途及優點

編輯

同像性的一個優點是,向這個語言擴展新概念變得更加簡單,因為表示代碼的資料,可在程序的層和基礎層之間傳遞。函數的抽象語法樹,可以作為元層中的資料結構來合成和操縱,然後再被求值。它可以更容易理解如何操縱代碼,因為它可以被理解為簡單的資料(因為語言本身的格式同於資料格式)。

同像性的典型演示是元循環求值器

實作方法

編輯

所有范紐曼型架構的系統,其中包括絕大多數當今的通用計算機,由於原始機器代碼在記憶體中的執行方式,其資料類型是位元組,故而可以隱含地描述為具有同像性。但是這個特徵也可以在編程語言層別上抽象出來。

Lisp及其方言例如SchemeClojureRacket等,使用S-表達式來實現同像性。

其他被認為具有同像性的語言包括:

同像性語言的編程範例

編輯

Lisp使用S-表達式作為資料和源碼的外部表示。S-表達式可以用原始Lisp函數READ讀取。READ返回Lisp資料:列表、符號、數字和字串。原始Lisp函數EVAL使用以資料形式表示的Lisp代碼,計算副作用並得出返回結果。結果由原始Lisp函數PRINT打印出來,它從Lisp資料產生一個外部的S-表達式。下面示例採用Common LispSBCL實現。

以下Lisp示例,構造出的列表含有結構類型person:它有兩個屬性nameage,其類型分別是字符串和整數:

* (defstruct person name age)
PERSON

* (person-name (cadr (list (make-person :name "john" :age 20) (make-person :name "mary" :age 18) (make-person :name "alice" :age 22))))
"mary"

以下Lisp代碼示例,使用了列表、符號和數值:

* (* (sin 1.1) (cos 2.03))      ; 中綴表示法為 sin(1.1)*cos(2.03)
-0.39501375

使用原始Lisp函數LIST產生上面的表達式,並將變量EXPRESSION設置為結果:

* (defvar expression)
EXPRESSION

* (setf expression  (list '* (list 'sin 1.1) (list 'cos 2.03)) )  
(* (SIN 1.1) (COS 2.03))
; Lisp傳回並打印結果

* (third expression)    ; 表達式中的第三項
(COS 2.03)

COS這項變更為SIN

* (setf (first (third expression)) 'SIN)
SIN
; 變更之後的表達式為 (* (SIN 1.1) (SIN 2.03)).

求值表達式:

* (eval expression)
0.79888344

將表達式打印到字串:

* (princ-to-string expression)
"(* (SIN 1.1) (SIN 2.03))"

從字串中讀取表達式:

* (read-from-string "(* (SIN 1.1) (SIN 2.03))")
(* (SIN 1.1) (SIN 2.03))
24
; 傳回一個其中有列表,數字和符號的列表

Prolog

編輯

Prolog是同像性語言並且提供了很多反射設施。

1 ?- X is 2*5.
X = 10.

2 ?- L = (X is 2*5), write_canonical(L).
is(_, *(2, 5))
L = (X is 2*5).

3 ?- L = (ten(X):-(X is 2*5)), write_canonical(L).
:-(ten(A), is(A, *(2, 5)))
L = (ten(X):-X is 2*5).

4 ?- L = (ten(X):-(X is 2*5)), assert(L).
L = (ten(X):-X is 2*5).

5 ?- ten(X).
X = 10.

6 ?-

在第4行建立一個新子句。算符:-分隔一個子句的頭部和主體。通過assert/1將它增加到現存的子句中,即增加它到「數據庫」,這樣我們可以以後調用它。在其他語言中可以稱為「在運行時間建立一個函數」。還可以使用abolish/1retract/1從數據庫中移除子句。注意在子句名字後的數,是它可以接受的實際參數的數目,它也叫做元數

我們可以查詢數據庫來得到一個子句的主體:

7 ?- clause(ten(X),Y).
Y = (X is 2*5).

8 ?- clause(ten(X),Y), Y = (X is Z).
Y = (X is 2*5),
Z = 2*5.

9 ?- clause(ten(X),Y), call(Y).
X = 10,
Y = (10 is 2*5).

call類似於Lisp的eval函數。

Rebol可巧妙的演示將代碼當作數據來操縱和求值的概念。Rebol不像Lisp,不要求用圓括號來分隔表達式。下面是Rebol代碼的例子,注意>>表示解釋器提示符,出於可讀性而在某些元素之間增加了空格:

>> repeat i 3 [ print [ i "hello" ] ]
1 hello
2 hello
3 hello

在Rebol中repeat事實上是內建函數而非語言構造或關鍵字。通過將代碼包圍在方括號中,解釋器不求值它,而是將它當作包含字的塊:

[ repeat i 3 [ print [ i "hello" ] ] ]

這個塊有類型block!,並且使用近乎賦值的語法,可以進一步的將它指定為一個字的值,這種語法實際上可以被解釋器理解為特殊類型set-word!,並採用一個字跟隨一個冒號的形式:

>> block1: [ repeat i 3 [ print [ i "hello" ] ] ] ;; 将这个块的值赋值给字`block1`
== [repeat i 3 [print [i "hello"]]]
>> type? block1 ;; 求值字`block1`的类型
== block!

這個塊仍可以使用Rebol中提供的do函數來解釋,它類似於Lisp中的eval。有可能審查塊的元素並變更它們的值,從而改變要求值代碼的行為:

>> block1/3 ;; 这个块的第三个元素
== 3
>> block1/3: 5 ;; 设置第三个元素的值为5
== 5
>> probe block1 ;; 展示变更了的块
== [repeat i 5 [print [i "hello"]]]
>> do block1 ;; 求值这个块
1 hello
2 hello
3 hello
4 hello
5 hello

另見

編輯

參考文獻

編輯
引用
  1. ^ Wheeler, David A. Readable Lisp S-expressions. [2022-01-29]. (原始內容存檔於2022-01-29). 
  2. ^ McIlroy, Douglas. Macro Instruction Extensions of Compiler Languages. Comm. ACM. 1960, 3 (4): 214–220. doi:10.1145/367177.367223. 
  3. ^ Mooers, C.N.; Deutsch, L.P. TRAC, A Text-Handling Language. Proceeding ACM '65 Proceedings of the 1965 20th national conference. 1965: 229–246. doi:10.1145/800197.806048. 
  4. ^ Kay, Alan. The Reactive Engine (學位論文). University of Utah. 1969 [2014-03-28]. (原始內容存檔於2018-09-15). 
  5. ^ 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 Homoiconic Languages. [2020-04-23]. (原始內容存檔於2013-04-23). 
  6. ^ Lispy Elixir. 8thlight.com. [2022-01-29]. (原始內容存檔於2022-03-05). Elixir, on the surface, is not homoiconic. However, the syntax on the surface is just a facade for the homoiconic structure underneath. 
  7. ^ Why we created Julia. julialang.org. [2020-04-23]. (原始內容存檔於2019-02-19). We want a language that’s homoiconic, with true macros like Lisp, but with obvious, familiar mathematical notation like Matlab. 
  8. ^ metaprogramming. docs.julialang.org. [2020-04-23]. (原始內容存檔於2013-05-04). Like Lisp, Julia represents its own code as a data structure of the language itself. 
  9. ^ Shapiro, Ehud Y.; Sterling, Leon. The art of Prolog: advanced programming techniques. MIT Press. 1994. ISBN 0-262-19338-8. 
  10. ^ R Language Definition (PDF): 6, 2021-05-18 [2022-01-29], (原始內容 (PDF)存檔於2022-04-22), ... the semantics are of the FPL (functional programming language) variety with stronger affinities with Lisp and APL. In particular, it allows 「computing on the language」, which in turn makes it possible to write functions that take expressions as input, something that is often useful for statistical modeling and graphics. 
  11. ^ "expression: Unevaluated Expressions", [2022-01-29], (原始內容存檔於2022-01-29) 
  12. ^ Homoiconic languages (archived), in true Blue blog at Oracle
  13. ^ Ramsay, S.; Pytlik-Zillig, B. Code-Generation Techniques for XML Collections Interoperability. dh2012 Digital Humanities Conference Proceedings. 2012 [2020-04-23]. (原始內容存檔於2016-03-03). 
  14. ^ Metaprogramming in mathematica. Stack Exchange. [2020-04-23]. (原始內容存檔於2018-08-20). Mathematica is [...] Homoiconic language (programs written in own data structures - Mathematica expressions. This is code-as-data paradigm, like Lisp which uses lists for this) 
  15. ^ Notes for Programming Language Experts. Wolfram Language. Wolfram. 2017 [2020-04-23]. (原始內容存檔於2022-01-04). 

外部連結

編輯