Remove ads
来自维基百科,自由的百科全书
在计算机编程中,同像性(homoiconicity來自希臘語單詞,homo-意為相同,icon含義表像),是某些编程语言的特殊屬性,这意味着用此语言书写的程序,可用使用这个语言将其作为数据来操纵,因此只要阅读程序自身,就能推论出来这个程序的内部表示。该属性经常被归结成,这个語言将“代碼當作数据”。
在同像性編程語言中,程序的主要表示方式,也是属于这个語言自身的原始類型的一种資料結構。這使得在这种语言中的元編程,比在没有这个属性的语言中要更加容易:在这种語言中的反射(在運行時檢查程序的實體),取決於單一的、同質的結構,而且它不必去處理以複雜語法形式出现的其它一些結構。同像性语言典型的包括对语法宏的完全支持,这允许编程者以简明方式来表达程序变换。
Lisp編程语言,是具有同像属性的典型範例,它設計得易于進行列表操纵,而且其結構用具有嵌套列表形式的S-表达式来给出,它可以由其他LISP代码来操纵[1]。這類語言的其他例子有Clojure(一种現代流行的LISP方言),Rebol和Refal,以及最近的Julia等編程語言。
同像性一詞的原始來源,是論文《編譯器語言的巨集指令擴展》[2]。其依據是早期具影響力的論文《TRAC文本處理語言》[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及其方言例如Scheme,Clojure,Racket等,使用S-表達式來實現同像性。
其他被认为具有同像性的语言包括:
Lisp使用S-表達式作為資料和源碼的外部表示。S-表達式可以用原始Lisp函數READ
讀取。READ
返回Lisp資料:列表、符號、數字和字串。原始Lisp函數EVAL
使用以資料形式表示的Lisp代码,計算副作用並得出返回結果。結果由原始Lisp函數PRINT
打印出來,它從Lisp資料產生一個外部的S-表達式。下面示例采用Common Lisp的SBCL实现。
以下Lisp示例,构造出的列表含有结构类型person
:它有两个属性name
和age
,其类型分别是字符串和整数:
* (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
; 傳回一個其中有列表,數字和符號的列表
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/1
或retract/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
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.