热门问题
时间线
聊天
视角

Simula

来自维基百科,自由的百科全书

Simula
Remove ads

Simula,一種編譯式程式語言,由奧利-約翰·達爾克利斯登·奈加特,在1960年代於挪威奧斯陸挪威計算中心英語Norwegian Computing Center,開發出來了Simula I與Simula 67兩代。它承繼了ALGOL 60作為基礎,被認為是第一個物件導向程式設計的程式語言。

事实速览 編程範型, 設計者 ...

Simula 67介入了對象子類(後來慣稱為子類繼承超類)、虛過程[8],還有協程離散事件模擬和特徵性的垃圾收集[9]。Simula的影響經常被低估[10]Smalltalk[5]CLU[6]C++Object PascalModula-3Java和後來的很多編程語言,受到了Simula 67的啟發。BETA是Simula的現代後繼者。

Remove ads

歷史

Thumb
最初Simula設計的離散事件網絡的實例示意圖

1957年,克利斯登·奈加特開始在NDRE英語Norwegian Defence Research EstablishmentFerranti Mercury英語Ferranti Mercury電腦上寫模擬器,為此他需要更強大的程式語言。1962年1月,奧利-約翰·達爾開始跟他合作,他們受到ALGOL 60的啟發。1962年5月,發展出第一份模擬器程式語言提議,語言取名為SIMULA,後改稱為Simula I。此時,克利斯登·奈加特受到Sperry Rand公司英語Sperry Corporation邀請,去協助他們開發UNIVAC 1107英語UNIVAC 1100/2200 series電腦。UNIVAC軟體部門的主管鮑伯·貝莫英語Bob Bemer,力邀克利斯登·奈加特前往國際資訊處理聯盟英語International Federation for Information Processing(IFIP)舉行的第二次國際會議上,發表了論文「SIMULA-擴展ALGOL到離散事件網路的描述」[11]

1963年8月,挪威計算中心英語Norwegian Computing Center(NCC)購買到UNIVAC 1107英語UNIVAC 1100/2200 series,在UNIVAC的合約同意下,奧利-約翰·達爾在這台電腦上安裝以凱斯西儲 1107 ALGOL 60的編譯器來實作的Simula I。1965年1月,Simula I終於可以在UNIVAC 1107上完全的運作。Simula I在1968年被移植到了Burroughs英語Burroughs large systems B5500電腦,以及後來蘇聯的URAL-16英語Ural (computer)電腦。

1965年,東尼·霍爾首次提出「記錄」(record class)構造的概念[12],1966年,克利斯登·奈加特奧利-約翰·達爾二人,將Simula I的具有「准並行」性質的「進程」,擴展成了具有了記錄類性質的廣義進程,隨即改稱為「物件[13]。1967年5月,奈加特和達爾在奧斯陸舉辦的IFIP工作小組論壇中,發表了關於子類聲明的論文,形成Simula 67的第一份定義文件[14]

1968年召開的會議,組成了SIMULA標準小組,並發表了第一份官方Simula標準文件「SIMULA 67通用基礎語言」[15]。在1960年代後期和1970年代早期,Simula 67主要實現於四個系統之上:挪威計算中心英語Norwegian Computing CenterUNIVAC 1100系列英語UNIVAC 1100/2200 series,挪威計算中心的IBM System/360System/370英語IBM System/370奧斯陸大學Kjeller英語Kjeller聯合安裝的CDC 3000系列英語CDC 3000 series,和瑞典國防研究所英語Swedish National Defence Research Institute(FOA)的DEC TOPS-10

當前的Simula 67業界標準,是在1986年修訂的「標準SIMULA」[7],它在1987年被接受為瑞典國家標準[16]。它有四個主要實現:Simula AS英語Simula Research LaboratoryLund Simula、GNU Cim[3]和Portable Simula Revisited[2]

Simula影響了Smalltalk[5],和後來的面向對象編程語言[10]C++語言和Java語言的創始人,都認可自己受到了Simula的重要影響[17]

Remove ads

語法和語義

Simula 67包含通用算法語言ALGOL 60的多數特徵作為自己的子集[7],它是大小寫不敏感的。Simula 67的中心概念是對象,對象是自我容納英語Self-sustainability(self-contained)的一段程序即一個實例,它擁有由一個聲明定義的自己的局部數據和行動(action)。是一種過程[18],它有能力引起在它被調用後仍存活的一個塊實例,而這些實例就叫做這個類的對象。為了操縱對象和相互關聯對象,語言介入了鍊表處理設施。

Simula 67為了將整個程序執行組織為:對象所屬的諸「行動階段」的一個序列,而將其所必需的基本功能特徵,包含在作為「系統類」的「環境類」之中。在環境類中有被稱為「標準系統類」的模擬器Simulation,它定義了充當系統時間軸的「定序集合」(sequencing set)對象SQS進程Process和事件通告類EVENT_NOTICE,定序集合的成員是事件通告對象,它通過其PROC特性提及一個進程對象;事件通告表示了對應這個進程對象的下一個行動階段的一個事件,被調度在系統時間EVTIME時發生。

Remove ads

是成序列的聲明,跟隨着成序列的語句,並被包圍在關鍵字beginend之間。塊自身是一種語句[19],在Simula 67中,它包括子塊和有前綴(prefixed)塊,在語法上,子塊就是無前綴的ALGOL 60的塊,即匿名塊和過程主體。

聲明擔負定義在程序中用到的(quantity)的特定屬性,並給它們關聯上標識符,這包括:簡單變量、數組、接轉(switch標籤列表)、過程、和外來聲明。塊中的每個語句,都可以是塊或複合語句。複合語句與塊相比在語法上沒有聲明。

所有的塊,自動地介入名稱目錄英語Nomenclature(nomenclature)的一個新的層級:在這個塊內出現的標識符,可以通過合適的聲明,而被指定為局部於所論及的這個塊;這個標識符在這個塊裡面的所表示的實體,不存在於它的外面;這個標識符在這個塊外面的所表示的任何實體,在這個塊裡面是不可見的;在Simula 67中,可通過連接或遠程訪問使它成為可見的。

除了表示標籤的標識符之外,一個標識符,如果出現在一個塊中,而且並非聲明於這個塊中,則它非局部英語Non-local variable於這個塊。因為塊中的語句自身可以是一個塊,局部和非局部於一個塊的概念,必須遞歸地去理解。

是一種形式描述或模式,關乎匯聚英語Aggregate數據結構和關聯的算法以及行動[20]。當一個塊執行的時候,生成這個塊的一個動態實例[21]。一個塊實例,可以被認為是它的形式描述的文字複本。在計算機中,一個塊實例可以採用某一種形式的內存區域,它包含需要的動態塊信息,並包括空間來持有局部於這個塊的變量的內容[22]

塊的任何一個內部的塊,仍然是一個模式,但是在其中出現的非局部英語Non-local variable標識符,標定了局部於在字面上外在包圍(textually enclosing)的塊實例的項目[23]非局部英語Non-local variable於內部的塊的標識符綁定,對這個內部的塊的任何隨後的動態實例,保持有效[24]

塊實例中的局部變量,標識了分配給塊實例的內存片段。在進入一個塊之中的時候,任何局部於這個塊即在其中聲明的變量,都會被初始化。局部於一個塊的一個變量,是一個內存設備,其內容依據這個變量的類型,要麼是一個,要麼是一個引用

類型可特徵化為直接關聯着一個可能值的集合,即這個類型的「值範圍」。引用概念,對應於一個名字或一個「指針」的直觀想法。在Simula 67中有兩種引用類型對象引用類型和文本引用。對於值類型不關聯着引用概念。提供了引用值的機制,還反映了機器尋址可能性;在特定簡單情況下,一個引用可以被實現為一個存儲的值的內存地址

過程

過程與塊相比,它有一個名字,可以在程序的一些不同地方提及,並且在調用時可以給出參數[25]。過程和塊都不可能建立到它或它內部的引用[25];對於一個給定塊,有可能生成它的一些可以共存和交互的實例[22],例如遞歸過程的實例。

過程的參數傳送模態,除了有傳值調用傳名調用[26],在Simula 67中,又增加了傳引用調用。過程的缺省的傳送模態,在Simula 67中,對於值類型的參數是傳值調用,對於所有其他類型的參數是傳引用調用;故而在過程聲明的參數規定中,增加了以name為前導的名字部份,用來指定所述及的參數採用傳名調用。

在過程主體內傳名調用的形式參數的每次出現,都引起對實際參數的一次求值。在Simula 67中,這個求值發生在過程語句的上下文中,就是說不會出現標識符衝突,因為過程主體和它的變量此時是不可見的。過程調用的執行,在有參數的情況下要經歷如下步驟:建立形式參數塊實例;求值對應於傳值調用或傳引用調用的實際參數,並將其結果賦值給形式參數塊實例的對應變量;過程主體被初始化並開始執行。

真正(proper)過程,不定義特定類型的函數指定式的值,在Simula 67中,它被稱為具有普遍(universal)類型,任何類型都從屬(subordinate)於普遍類型。

Remove ads

類與對象

類聲明定義一個程序(數據和行動)模式,而符合(conforming)這個模式的對象被稱為「屬於相同的類」。不同於Smalltalk 80面向對象編程語言,Simula 67的類和後來的Modula-3的對象類型,不進行實例變量英語Instance variable類變量英語Class variable的區分。

對於一個給定對象,類聲明中形式參數、在虛擬部份中規定的虛擬,和聲明為局部於類主體(body)的量,叫做這個對象的特性(attribute)。一個特性的聲明或規定叫做特性定義。在1986年修訂的語言標準中,通過保護規定可以限制類特性標識符的可見性。

類聲明中的形式參數,不適用傳名調用,這是為了保證實際參數在類所引起的對象存活期間不能消失[27]。在規定部份中需要對每個形式參數進行規定,這些參數被當作局部於類主體的變量。類主體通常是一個,即使它如語法形式所允許的那樣是塊以外的一個語句,也表現得如同一個塊。一個分裂(split)主體表現為一個塊,在其中符號inner表示一個虛設(dummy)語句。

Simula 67的類主體中除了有定義特性的聲明,還可以有定義行動的語句。如果在一個類聲明中有行動定義,則符合這個模式的行動,可以由屬於這個類的所有對象執行。如果在類聲明中沒有指定行動,則定義了一類純數據結構,很多後來的面向對象編程語言就是這樣,將對象作為記錄結構的擴展。例如:

class Order(number); integer number;
begin
    integer numberOfUnits, arrivalDate;
    real processingTime;
end;

屬於特定類的對象,通過對象表達式中的對象生成式new來生成,並製作出到它的引用。例如,想要多少就能生成多少屬於這個Order類的一個新對象:

new Order(103);

屬於一個對象的行動,可以都在就一個過程而言的一個序列中執行。這些行動,還可以作為一系列的獨立子序列或「行動階段」(active phase)來執行。在一個給定對象的兩個行動階段之間,可以出現任何數目的其他對象的行動階段。

Remove ads

子類與類串接

一個類可以被用作到另一個類聲明的「前綴」,從而將前綴所定義的性質,建造入這個新的類聲明定義的對象之中。具有前綴類C和類標識符D的一個類聲明,定義了類C的一個子類D。屬於子類D的對象,由自身是類C的對象的「前綴部份」,和類D的類聲明主體所描述的「主體部份」組成。這兩部份串接而形成了一個複合對象,它可以用串接成的類聲明來正式的描述,串接的過程被認為先於程序執行而發生。

類只能在定義它的塊層級中用作前綴。前綴類自身也可以有前綴。類不能出現在自己的前綴序列中。子類被稱為內部(inner)於前綴類,而前綴類被稱為外部(outer)於子類,但不可將子類稱為前綴類的內部類。這不同於後來的面向對象編程語言中的稱謂:「派生類」擴展「基礎類」,或「子類」繼承「超類」。例如:

Order class SingleOrder;
begin
    real setupTime, finishingTime, weight;
end;
SingleOrder class Plate;
begin
    real length, width;
end;

屬於Order類的子類SingleOrder類和Plate類的新對象,都含有Order類定義的數據,再加上在各種類聲明中定義的補充數據。例如,屬於Plate類的對象含有Order類、SingleOrder類和Plate類定義的所有數據。

一個標識符在一個塊中的出現,如果並未處在遠程標識符之中或在有重新定義的內部的塊之中,則被稱為「未託付」(uncommitted)出現。對於子類的聲明及其前綴類串接成的類聲明,在局部於子類的特性標識符,和局部於其前綴類的特性標識符,二者的未託付出現之間的可能衝突,將憑藉對涉及到的局部於子類的特性標識符的適合的系統性變更來避免,但是對應虛擬的標識符不變更。

對於前綴類和子類聲明串接成的類聲明,它的形式參數列表,由前綴類的形式參數列表,和附加的子類聲明的參數列表二者合併而成。它的值部份、規定部份和虛擬部份,是前綴類和子類聲明相應各部份的併集。結果的虛擬部份不能包含某個標識符的多於一次出現。

對於類主體,最初的begin和隨後的聲明被稱為「塊頭部」,從第一個可執行語句直到inner之前的語句被稱為「初始運算」,在inner之後的語句和最終的end被稱為「複合尾部」。如果前綴類主體是分裂主體,用前綴類的塊頭部複本替代子類的塊頭部中的begin,將前綴類的初始運算複本插入到子類的塊頭部之後,用前綴類的複合尾部複本替代子類的複合尾部中的end。如果前綴類主體不是分裂主體,它被解釋為如同將;inner插入到最終的end之前。

Cn是具有前綴序列C1, C2, ……, Cn-1的一個類,這裡類Ck (k = 1, 2, ……, n)的下標k叫做前綴層級,X是屬於類Cn的一個對象,則X是一個複合對象;非正式的說,串接機制有如下結果:

  • 對象X擁有的特性集合,是在序列C1, C2, ……, Cn中諸類所定義特性集合的併集。在類Ck (1 <= k <= n)中定義的特性,被稱為定義在前綴層級k
  • 對象X擁有的「運算規則」,由來自這些類主體的諸語句構成,它們所屬的前綴層級遵循規定的次序。來自類Ck的語句,被稱為屬於對象X的前綴層級k
  • 在對象X的前綴層級k的語句,能訪問在對象X的等於或外部於k的前綴層級上定義的它的所有特性,但是如果外部的類Ci (i < k)中存在衝突定義,則導致對應特性不可見而不能直接訪問。這些不可見特性仍有訪問方式,例如通過使用過程或this Ci
  • 在對象X的前綴層級k的語句,不能立即訪問在對象X的內部於k的前綴層級上定義的它的那些特性,除非通過虛擬
  • 在前綴層級k的分裂主體中,符號inner,表示對象X的屬於內部於k的前綴層級的運算規則的諸語句,或者在k = n時表示虛設語句。如果序列C1, ……, Cn-1都沒有分裂主體,則在對象X的運算規則中諸語句所屬的前綴層級遵循遞增次序。

用作前綴的系統類,與其前綴鏈的所有的類一起,被當作聲明於最小的包圍其字面上出現的塊中。因此重新聲明可以出現在一個程序的內部的塊層級中。

有前綴塊的一個實例,是由塊前綴生成的一個單一的對象,與這個主體塊的實例,串接而成一個複合對象,其串接定義採用類似於類的串接規則。塊前綴的類的形式參數,按塊前綴的實際參數所示初始化。對有前綴塊有兩個限制:一個類如果在其中通過使用this來引用類自身,則它作為塊前綴是非法的。塊前綴的類標識符所提及的類,必須局部於最小的包圍這個有前綴塊的塊。

Remove ads

對象引用

當屬於各種類的很多對象,作為同一個程序的各個部份而共存的時候,需要能夠對個體對象指定名字,為此介入了叫做「引用」的新的基本類型;還要能相互關聯對象,比如通過二叉樹和各種其他類型的列表結構。Simula 67將循環雙向鍊表叫做「集合」,它將集合類Simset作為「標準系統類」介入為環境類的一部份。

對於某個類的一個對象,有一個唯一的關聯的「對象引用」標定這個對象,並且對於任何類C,都有一個關聯的對象引用類型ref(C)。這種類型的,被稱為由類C限定;它的值,要麼是一個對象,要麼是表示「沒有對象」的特殊值none

限定(qualification)將值的範圍,限制至包括在限定類中的這些類的對象。不管何種限定,值的範圍都包括值none。一個對象引用的限定,是一個類標識符,它因此服從聲明的作用域和可見性英語Visibility (disambiguation)規則。限定關聯於所提及的類聲明所局部於的那個塊的一個實例。這蘊涵着特定的關於限定有效性的檢查,不能單獨在程序正文的基礎上進行,因此這種測試必須在程序執行期間完成。

引用是有限定的,蘊涵着一個給定引用所提及的對象,只能屬於這個限定中提到的類,或屬於這個限定類的子類。如果一個對象引用類型的限定,是另一個對象引用類型的限定類的子類,則前者被稱為從屬於後者。例如:

ref(Order) next, previous;

對象表達式是獲得對一個對象的引用的一種規則。對象表達式的值,是被引用的對象或none運算符:-讀作「指稱」(denote),它指示將一個引用指派(assign)到處在「引用指派符」左側的引用類型變量過程標識符。例如:

next :- new Plate(50);
previous :- next;
next :- none;

而運算符:=讀作「成為」(become),指示將一個,指派到處在賦值符左側的值類型的變量或過程標識符,或者將一個文本值,指派到在賦值符左側者所引用的那個文本(frame)。

Remove ads

遠程訪問

一個對象的一個特定特性,由下列三項信息來完全標定(identify):

  1. 這個對象
  2. 外部於或等於這個對象的類的一個
  3. 在這個類中或在屬於它的前綴序列的任何類中定義的一個特性標識符

對於任何特性標定,第二項類信息都是在字面上定義的,從而形成靜態綁定[28]。屬於其他對象的特性,可以要麼單個的通過「遠程標識符」(「點表示法」),要麼成組的通過「連接」(connection)機制,從其外面「遠程訪問」來引用和使用。

遠程標識符的形式為:简单对象表示式.特性标识符,它可用作簡單變量、數組變量、函數指定式(designator)和過程語句中的標識符,這種點表示法給出對信息的單獨片段的訪問,例如:

if next.number >= previous.number then ……;

它將名為next的類Order的對象的特性number,比較於名為previous的類Order的對象的特性number

「成組訪問」可通過連接語句inspect來完成,例如:

inspect next when Plate do begin …… end;
inspect new Plate(50) do begin …… end;

在這裡的beginend之間的語句中,next引用的對象所屬的Plate類的所有信息片段,都可以直接提及。

do後面的語句,如果是塊以外的一個語句,它表現得也如同一個塊。連接語句進而表現得如同被叫做「連接塊」的第二個虛構(fictitious)的塊所包圍。連接機制的用途,是為在連接塊中的特定特性標定,提供對象信息和在when子句中的類信息的隱式定義。

設對象表達式求值為X,在連接塊執行期間,稱對象X為「被連接上」了。連接塊有一個關聯的「塊限定」,如果連接塊有when子句,它是前面的類標識符,如果沒有when子句,它是前面的對象表達式的限定。

Remove ads

虛過程

虛擬(virtual)量,是在類聲明中由virtual:前導的,它有雙重用途:

  • 允許在一個對象的一個前綴層級上,訪問在內部前綴層級上聲明的特性。
  • 允許在一個前綴層級上的特性重新定義,在外部前綴層級上有效。

不同於一般的特性標定,虛過程形成「半動態綁定」[8],在某種意義上是類似傳名調用的機制[27]。這種機制不同於Smalltalk 80面向對象編程語言中,上溯子類至超類的繼承鏈來找到最近的方法實現的那種動態綁定

一個對象的一個虛擬量,要麼是「無匹配的」的,要麼被標定為具有一個「匹配」特性,它是在這個虛擬量的前綴層級上或在內部前綴層級上聲明的,其標識符重合英語Coincidence(coincide)於虛擬量的標識符的一個特性。匹配特性必須與虛擬特性有相同的種類。在一個給定前綴層級上的匹配量的類型,必須重合或從屬於這個虛擬規定的類型,和在任何外部前綴層級上聲明的任何匹配量的類型。

在1986年修訂的語言標準中,虛過程量可選的可使用procedure <过程标识符> <过程规定>方式來指定,即憑藉它的類型,並且如果它有形式參數,則還憑藉它們的類型、種類和傳送模態。含有過程規定is <过程声明>的虛過程量,只能由有相同類型的過程來匹配,並它與這個過程規定具有相同的過程頭部。

例如,下面實現散列表HashTable類,聲明了整數類型的虛過程hash並隨即實現了它,又定義了要用到這個散列函數hash的查找過程lookup:

class HashTable(n); integer n;
    virtual: integer procedure hash;
begin 
    integer procedure hash(t); text t;
    begin 
        ……
    end hash;
    text array table(0 : n-1); ……
    integer procedure lookup(t, old);
        name old; Boolean old; text t;
    begin
        integer i, istart; Boolean entered;
        i := istart := hash(t);
        while not entered do
        begin
            ……
        end;
        lookup := i;
    end lookup;
end HashTable;
HashTable class ALGOL_hashing;
begin
    integer procedure hash(t); text t;
    begin
        ……
    end hash;
end ALGOL_hashing;

作為子類的ALGOL_hashing類和以HashTable類為前綴的有前綴塊,可以用自己的hash過程實現,替代在HashTable類中的實現。在子類的類主體和有前綴塊的主體塊中,能獲得到過程lookup,而它用到的過程hash是這裡提供的替代實現。

對象表達式

對象表達式英語Expression (computer science),具有類型ref(限定)。對象表達式的限定規則包括:對象生成式(generator)、局部對象或限定對象,分別由跟隨符號newthisqua的標識符的類來限定(qualification)。

對象生成式new C,這裡標定了類C,它涉及到屬於類C的對象的生成和執行。這個對象是類C的一個新實例。對象生成式的求值構成自如下行動:

  1. 生成這個對象,並且如果這個對象生成式有實際參數,則求值它們,將得到的這些值及或英語And/or引用傳送給形式參數。
  2. 控制經過這個對象最初的begin進入其中,它籍此變成在「系附」狀態下運行。當所生成的對象執行了detach基本過程變為「脫離」狀態,或者經過這個對象最終的end退出而變為「終止」狀態,對象生成式的求值完成。

局部對象this C,這裡的C是類標識符。如果用在類CC的任何子類的類主體中,或用在其塊限定為類CC的一個子類的連接塊中,則這個對象表達式是有效的。在一個給定的上下文中,一個局部對象的值,是一個對象或連接到一個對象,它是這個對象表達式在其中有效的、最小的在字面上外在包圍的塊實例;如果沒有這樣的塊,則這個對象表達式在這個上下文中是非法的。對於一個過程或類主體的實例(上下文),「在字面上外在包圍」(textually enclosing)意為包含它的聲明。

暫瞬限定X qua C,這裡的X表示一個簡單的引用表達式,而C是類標識符。設類D是對象X的限定,對於限定對象X qua C,如果類C外部於或等於類D,或者是D的一個子類,則這個對象表達式是合法的,否則是為非法。如果X的值是none,或者是屬於外部於C的類的一個對象,則這個對象表達式求值形成一個運行時間錯誤;在其他情況下,X qua C的值,就是X的值。對一個串接的類對象的暫瞬限定,限制或擴展它的特性通過檢視或遠程訪問的可見性。

對象關係表達式,使用運算符isin,來測試一個對象的類成員關係。關係X is C,在X引用的是屬於類C的對象之時,值為true,否則值為false。關係X in C,在X引用的是屬於類C的對象,或者X是內部於類C的類之時,值為true,否則值為false

不同於算術關係表達式使用比較運算符=<>,對象引用關係表達式,使用比較運算符===/=,來比較引用,從而區別於對應的被引用的值。兩個對象引用XY,在它們都引用相同的對象,或者它們都是none之時,被稱為是「同一英語Identity (object-oriented programming)」的。關係X == Y,在這種情況下擁有的值為true,否則值為false。關係X =/= Y的值,是X == Y的值的否定

Remove ads

作用域和可見性

不同於ALGOL 60規定了作用域可見性英語Visibility (disambiguation),在Simula 67中,一個標識符定義及其關聯的標識符,在其作用域與可見性之間需要做出區別。

  • 作用域:一個標識符定義的作用域,是它在其中可能有作用的那一部份程序正文。同一個標識符,可以定義在程序的很多地方,因此可以關聯於不同的。同一個標識符的這些定義的作用域,因而可能有所重疊,例如在一個標識符在內部塊中被重新聲明的情況下。
  • 可見性英語Visibility (disambiguation):一個標識符定義,如果它所關聯的標識符,在程序的給定點上能夠提及到這個定義所聲明的,則它被稱為在這個點上是可見英語Visibility (disambiguation)的。在給定標識符於此可見的程序正文的一個特定點上,最多只能有一個定義關聯於這個標識符,例如在重新聲明的情況下,在它們作用域的併集內任何給定點上,只有一個定義是可見的。

一個標識符定義的局部塊,是在字面上最近的包圍塊,即子塊、前綴塊、過程主體或類主體,還包括圍繞for語句的受控語句、過程聲明、類聲明和連接塊等的虛構塊。這個標識符和它的定義,被稱為局部於這個塊。 標識符定義,只在它們的作用域內是可見的。一個特定定義的可見性,在它的作用域內可以受到下列限制:

  • 在一個標識符定義的局部塊所包圍的某個構造內,出現的具有相同標識符的標識符定義,是這個標識符的重新定義。在它們共同的作用域內,只有最內部的重新定義是可見的。
  • 一個重新定義,出現在類的某個內部前綴層級。
  • 遠程訪問,可以導致某些標識符定義在檢視塊或點表示法內變成不可見的。
  • 使用thisqua,可以導致一個或多個重新定義被暫時停止。
  • 在這個標識符定義所局部於的類聲明中,這個標識符出現在由hidden及或protected前導的保護部份中。

一個標識符的重新定義,不允許出現在它的局部塊的頭部,這禁止了在同一個塊中出現相同標識符的兩個定義。

Remove ads

塊實例

一個程序執行英語Execution (computing)的各個組成部份,都是即子塊、有前綴塊、連接塊和類主體的動態實例。一個塊實例,被稱為「局部於」直接包含它的描述正文的那個塊實例。例如一個給定類的實例,局部於包含這個類聲明的塊實例。最外層塊的實例不局部於塊實例。

在任何時間,「程序順序控制」(Program Sequence Control),首字母簡寫為PSC,參照在一個塊實例中當前被執行的程序點;形象的說,PSC「定位」至這個程序點,並被這個塊實例所「包含」。進入任何塊,都涉及到生成這個塊的一個實例,於是PSC進入這個塊實例,到達它的第一個可執行語句上。

一個塊實例在任何時間,都是處在四種執行狀態英語State (computer science)之一:系附(attached)、脫離(detached)、恢復(resumed)或終止(terminated)。沒有在字面上給出前綴的任何類,都被定義為前綴了一個虛構(fictitious)的類,它只有一個特性即detach過程,因此所有類對象或有前綴塊的實例,都擁有detach過程。在環境ENVIRONMENT中,還定義了用於調度call過程和resume過程。

  • 一個非類塊實例,總是在系附狀態下,這個實例被稱為系附到了導致它生成的那個塊上。因此一個過程體的實例,系附到包含對應函數指定式或過程語句的塊實例上。非類、非過程塊實例,系附到它所局部的塊實例。在PSC到達非類塊實例的最終的end的時候,PSC返回到緊隨導致這個塊實例生成的語句或表達式的程序點。
  • 一個對象,最初是在系附狀態下,並被稱為系附到包含對應對象生成語句的那個塊實例上。一個對象,可以通過執行detach過程,進入脫離狀態。通過執行call過程,可以使一個脫離狀態的對象重新進入系附狀態,它藉此變為系附到包含這個調用語句的那個塊實例上。通過執行resume過程,可以使一個脫離狀態的對象進入恢復狀態。不使用detachcallresume過程的一個程序執行,是一個簡單的系附狀態的塊的嵌套結構。
  • 當PSC通過一個對象的最終end,或通過goto語句離開它的時候,這個對象進入終止狀態。沒有塊實例系附到一個終止狀態的類對象上。終止狀態的對象,仍作為一個數據項而存在,它可以通過針對這個對象的包括過程和函數特性的這些特性的遠程標定來引用。

每當一個塊實例不復存在,局部於或系附於它的所有塊實例也不復存在。一個對象的動態作用域,因而受限於它的類聲明的作用域。在Simula 67最初標準中曾提到過,語言實現可以使用垃圾回收技術[9],來進一步縮減一個對象的有效壽命的跨度。一個數組聲明的動態作用域,可以擴展超出包含這個聲明的塊實例的作用域,因為傳引用調用參數傳送模態適用於數組。

准並行系統

一個準並行系統[29],由「構件」(component)構成。構件是塊實例組成的嵌套結構,其中標定(identify)這個構件的那個塊實例,叫做「構件頭領」。在每個系統中,構件中總是有一個被稱呼為「主構件」,在Simula 67最初標準中它叫做「主程序」,其他構件叫做「對象構件」。

構件的定序(sequencing),由detachcallresume過程支配。detach過程針對隱式指定的一個對象進行操作,而callresume過程顯式指定所操作的對象。

一個準並行系統,由包含局部的類聲明的一個子塊或有前綴塊的任何實例所標定。標定了一個系統的塊實例,叫做「系統頭領」。一個系統的主構件的頭領(head),重合英語Coincidence(coincide)於系統頭領。最外層的標定了一個系統的塊實例,被稱為「最外層系統」。

一個系統的主構件的頭領,總是處在系附狀態。一個系統的對象構件的頭領,確切的就是局部於系統頭領的那些脫離狀態的或恢復狀態的對象。在任何時間,在一個系統的構件之中,有確切的一個構件被稱為「生效」(operative)的。不生效的構件,有關聯的「重新激活點」,它標定在這個構件被激活(activate)時,要在它那裡繼續執行的程序點。一個對象構件是生效的,當且僅當這個構件的頭領處在恢復狀態。

稱謂一個塊實例P被一個塊實例X「動態包圍」,當且僅當存在一個塊實例序列:(P = Z0), Z1, ……, Zn-1, (Zn = X) (n >= 0),使得對於i = 1, 2, ……, n有着:Zi-1系附到Zi;或者Zi-1是一個恢復狀態的對象,它關聯的系統頭領系附到Zi。終止狀態和脫離狀態的對象,不被除了自身之外的塊實例動態包圍。

將當前包含PSC的塊實例動態包圍起來的塊實例鏈,叫做「運行」。在運行鏈上的塊實例,被稱為是「運行」(operating)的,最外層的塊實例總是運行的。一個構件只要其頭領是運行的它就是運行的。

一個系統如果有一個構件是運行的,它就是運行的;在任何時間,一個系統最多有一個構件是運行的;運行的系統的頭領,可以是不運行的。一個運行的構件總是生效的;如果一個系統的生效構件是不運行的,則這個系統也是不運行的。在不運行的系統中的生效的構件,是當這個系統成為不運行的時候在運行的構件,當這個系統再次成為運行的時候它將仍是運行的。

對於一個不生效的構件C,設它的頭領是X,如果一個塊實例P包含了它的重新激活點,則PX動態包圍,並且P除了自身之外不動態包圍塊實例。由構件頭領X動態包圍的這個塊實例序列,被稱為構件C的「重新激活鏈」。除了X之外,這個鏈上的所有構件頭領,標定了生效而不運行的構件。在構件C成為運行的時候,在它的重新激活鏈上所有塊也成為運行的。

除了系統構件,程序執行還可以包含不屬於特定系統的「獨立對象構件」。任何這種構件的頭領,都是一個脫離狀態的對象,它局部於一個類對象或一個過程主體實例,也就是說不局部於某個系統頭領。根據定義,獨立構件總是不生效的,只能對它執行call過程。

協程和生成器

准並行系統的detach/resume機制,是一種協程[30]。在Simula 67最初標準中,沒有恢復狀態,並且沒有規定call過程,但call過程通常會出現在當時的語言實現中[31]detach/call機制,相當於現在稱為生成器的「半協程」,而最初標準中的resume過程可理解為detach過程與call過程的組合[32]。1986年修訂的語言標準,增補定義了call過程,新增了恢復狀態,並且重新定義了resume過程,它不可再理解為detach過程與call過程的組合。

准並行系統,被創建於進入包含局部的類聲明的一個子塊或有前綴塊的時候,藉此生成的實例成為這個新系統的頭領。初始時,這個主構件是生效的,並且是這個系統唯一的構件。

建立一個對象構件,要通過針對一個系附狀態的對象,執行detach過程,藉此PSC返回到這個對象系附到的那個塊實例。這個對象進入脫離狀態,並成為一個新的不生效的構件的頭領,這個構件的重新激活點被定位到緊後於這個detach過程調用。如果這個對象局部於一個系統頭領,則這個新的構件成為這個關聯的系統的成員

通過detachcall過程,可以形成「半對稱定序」,這隻涉及到對象構件,而不區分它們是屬於系統的構件還是獨立的構件。

  • 對於一個不生效的對象構件,通過針對它的脫離狀態的頭領,執行一個call過程,可以重新激活這個構件,藉此PSC移動到它的重新激活點上。這個頭領重新進入系附狀態,並變成系附到包含這個call過程調用的塊實例上。這個構件正式的失去了本身(作為構件)的狀態。

通過detachresume過程,可以形成「對稱構件定序」,這隻涉及到屬於一個準並行系統的那些構件。對立於半對稱定序中的「調用者」與它的「被調用者」,在對稱構件定序中的「恢復者」與它的「被恢復者」,具有完全的對稱聯繫。

  • 對於這個系統的一個不生效的對象構件,通過針對它的脫離狀態的頭領,執行一個resume過程,可以重新激活這個構件,藉此PSC移動到它的重新激活點上;這個構件的頭領進入恢復狀態,而這個構件變成生效的。這個系統以前生效的構件變成不生效的,並且它重新激活點被定位到緊後於這個resume過程調用;如果這個構件是一個對象構件,則它的頭領進入脫離狀態。
  • 對於當前生效的對象構件,通過針它的恢復狀態的頭領,執行一個detach過程調用,這個系統的主構件重獲生效的狀態,藉此PSC移動回到主構件的重新激活點上。以前生效的構件變成不生效的,並且它重新激活點被定位到緊後於這個detach過程調用。這個構件的頭領進入脫離狀態。

PSC經過一個類對象的最終end的效果,除了使這個對象成為終止狀態,而非脫離狀態之外,相同於針對這個對象執行detach過程的效果。其結果是它不會得到重新激活點,並且在它已經擁有作為構件頭領的狀態時,失去這種狀態。

程式範例

Hello, World!

Simula 67下的經典Hello, World!範例:

begin
    outtext("Hello, World!");
    outimage;
end;

outtext過程將字符串輸出到緩衝區,而outimage過程將緩衝區內容輸出到標準文件,二者都定義在輸出文件類OutFile中,而它是文件類File的子類。

二叉樹

下面例子中的Tree類,是二叉數據結構實現:

class Tree(val); integer val;
begin
    ref(Tree) left, right;
    procedure insert(x); integer x;
    begin
        if x < val then
        begin
            if left == none then
                left :- new Tree(x)
            else 
                left.insert(x)
        end
        else if right == none then
            right :- new Tree(x)
        else
            right.insert(x);
    end insert;  
    ref(Tree) procedure find(x); integer x;
    begin
        if x = val then
            this Tree
        else if x < val then
            (if left == none then
                none
             else 
                left.find(x))
        else if right == none then
            none
        else
            right.find(x);
    end find;
end Tree;

find過程的主體中出現了表達式this Tree,它產生的值所引用的是當前節點。這裡通過函數指定式X.find(x),來調用對象Xfind過程,如果X.va​l = x,則這個函數的結果是到X自身的引用值。

協程

Simula 67標準通過如下實例來詮釋叫做「准並行系統」的協程機制:

begin comment 系统S1;
    ref(C1) X1;
    class C1;
    begin
        procedure P1;
            detach;
        P1
    end C1;
    ref(C2) X2;
    class C2;
    begin
        procedure P2;
        begin
            detach; 
            ! 可尝试detachresume(X1);
        end P2;
        begin comment 系统S2;
            ref(C3) X3;
            class C3;
            begin 
                detach;
                P2
            end C3;
            X3 :- new C3;
            resume(X3)
        end S2
    end C2;
    X1 :- new C1;
    X2 :- new C2;
    call(X2); ! 可尝试resume(X2);
end S1;

這個例子程序中,有兩個准並行系統S1S2。系統S1是對應最外層子塊的最外層系統,它包含了兩個類聲明C1C2。系統S2對應於類C2的類主體中的匿名塊,它包含了一個類聲明C3

在PSC進入最外層子塊開始處,產生系統S1的系統頭領,到達第28行的系統S1主構件的第一個可執行語句,開始生成對象X1,進入類C1,PSC到達第7行的類主體的第一個可執行語句,調用了類C1的特性過程P1,進入第6行的過程體的第一個可執行語句,這時的狀況是:

[S1] ← (X1) ← (P1) ← PSC

這裡方括號表示系統頭領,圓括號表示其他種類的塊實例,左向箭頭表示系附,使用粗體表示這個塊實例執行了對象生成式。執行第6行的detach語句後,對象構件頭領X1進入脫離狀態,保存X1的重新激活點為第6行P1過程體結束,回溯到第28行對象產生式結束,系統S1的對象X1生成完畢,這時的情況是:

[S1] ← PSC
 |
(X1) ← (P1) ← <X1重新激活点>

這裡的豎槓將對象構件頭領列為一個系統的成員。PSC到達系統S1主構件第29行,開始生成對象X2,進入類C2,PSC到達第17行的類主體中匿名塊開始處,進入這個子塊產生系統S2的系統頭領,PSC到達第24行的系統S2主構件的第一個可執行語句,開始生成對象X3,進入類C3,PSC到達第20行類主體開始處,這時的情況是:

[S1] ← (X2) ← [S2] ← (X3) ← PSC
 |
(X1) ← (P1) ← <X1重新激活点>

執行第21行類主體的第一個可執行語句detach後,對象構件頭領X3進入脫離狀態,保存X3的重新激活點為第22行,回溯到第24行對象產生式結束,系統S2的對象X3生成完畢,這時的情況是:

[S1] ← (X2) ← [S2] ← PSC
 |             |
 |            (X3) ← <X3重新激活点>
 |
(X1) ← (P1) ← <X1重新激活点>

PSC到達系統S2主構件的第25行,執行resume(X3)語句後,保存系統S2主構件的重新激活點為第26行,對象構件頭領X3進入恢復狀態,PSC恢復到第22行的對象X3的類主體之中,這時的情況是:

[S1] ← (X2) ← [S2] ← <S2重新激活点>
 |             |
 |            (X3) ← PSC
 |
(X1) ← (P1) ← <X1重新激活点>

這裡的下劃線指示這個塊實例處在恢復狀態。執行第22行的類C2的特性過程P2,到達在第14行的過程體的第一個可執行語句,這時的情況如下,並標記為「狀況A」:

                                  ! 状况A ;
[S1] ← (X2) ← [S2] ← <S2重新激活点>
 |             |
 |            (X3) ← (P2) ← PSC
 |
(X1) ← (P1) ← <X1重新激活点>

執行在第14行P2過程體的detach語句後,對象構件頭領X2進入脫離狀態,保存X2的重新激活點為第15行,回溯到第29行對象產生式結束,系統S1的對象X2生成完畢,這時的情況如下,並標記為「狀況B」:

                                  !状况B ;
[S1] ← PSC
 |
(X1) ← (P1) ← <X1重新激活点>
 |
(X2) ← [S2] ← <S2重新激活点>
        |
       (X3) ← (P2) ← <X2重新激活点>

注意對象X3仍是系統S2的生效構件,它並沒有自己的重新激活點。序列(P2, X3, X2)X2的重新激活鏈,這裡的X3是恢復狀態,而它的系統頭領S2系附到了X2

PSC進入系統S1主構件第30行,執行call(X2)語句後,對象構件頭領X2進入系附狀態,PSC恢復為第15行的P2過程體之中,這時的情況重現為前面的「狀況A」(S1不加粗體)。

如果將系統S1主構件中第30行改寫為一個resume(X2),執行後對象構件頭領X2進入恢復狀態,保存系統S1主構件的重新激活點為第31行,PSC恢復為第15行的P2過程體之中,會出現如下情況:

[S1] ← <S1重新激活点>
 |
(X1) ← (P1) ← <X1重新激活点>
 |
(X2) ← [S2] ← <S2重新激活点>
        |
       (X3) ← (P2) ← PSC

序列(P2, X3, X2)X2的運行鏈。

如果將P2過程體中第15行的注釋替換為一個detach語句,執行後對象構件頭領X2進入脫離狀態,保存X2的重新激活點是第16行,PSC恢復到第31行的系統S1主構件之中,這時的情況重現為前面的「狀況B」。

如果將P2中第15行的注釋替換為一個resume(X1)語句,執行後對象構件頭領X2進入脫離狀態,對象構件頭領X1進入恢復狀態,保存X2的重新激活點為第16行,PSC恢復到第6行的P1過程體的過程結束,會出現如下情況:

[S1] ← <S1重新激活点>
 |
(X1) ← (P1) ← PSC
 |
(X2) ← [S2] ← <S2重新激活点>
        |
       (X3) ← (P2) ← <X2重新激活点>

抽象類

下面例子中,定義了一個字形Glyph,它是抽象基礎類,並且有二個實現子類字符Char英語Line (text file)Line

begin
    class Glyph;
        virtual: procedure print is procedure print;
    begin
    end;
    Glyph class Char(c);
        character c;
    begin
        procedure print;
            outchar(c);
    end;
    Glyph class Line(cs);
        ref(Glyph) array cs;
    begin
        procedure print;
        begin
            integer i;
            for i := 1 step 1 until upperbound(cs, 1) do
                cs(i).print;
            outimage;
        end;
    end;
    ref(Glyph) rg;
    ref(Glyph) array rgs(1 : 4);
    rgs(1) :- new Char('A');
    rgs(2) :- new Char('b');
    rgs(3) :- new Char('b');
    rgs(4) :- new Char('a');
    rg :- new Line(rgs);
    rg.print;
end;

這裡的虛過程量print具有過程規定is procedure print,它匹配既沒有形式參數也沒有結果值的print過程,如果不加以這種過程規定,則它可以匹配具有任何形式參數和任何結果值的print過程。在Simula 67中,沒有不可以實例化帶有純虛過程類的特定限制,因而缺乏真正的抽象類的概念,所有類都可以被實例化,但是調用純虛過程會產生運行時間錯誤

模擬器

在下面的離散事件模擬例子中,Sam、Sally和Andy正在逛商店買衣服,他們必須共享一個試衣間。他們每人只能瀏覽商店大約12分鐘,並接着獨占的使用試衣間大約3分鐘,每個行動都服從正態分布,他們的試衣間經歷被模擬如下:

Simulation
begin
    class FittingRoom;
    begin
        ref(Head) door;
        Boolean inUse;
        procedure request;
        begin
            if inUse then
            begin
                wait(door);
                door.first.out;
            end;
            inUse := true;
        end;
        procedure leave;
        begin
            inUse := false;
            activate door.first;
        end;
        door :- new Head;
    end;
    procedure report(message); text message;
    begin
        outfix(time, 2, 0);
        outtext(": " & message);
        outimage;
    end;
    Process class Person(pname); text pname;
    begin
        while true do
        begin
            hold(normal(12, 4, u));
            report(pname & " 要求用试衣间");
            fittingroom1.request;
            report(pname & " 已进入试衣间");
            hold(Normal(3, 1, u));
            fittingroom1.leave;
            report(pname & " 已离开试衣间");
        end;
    end;
    integer u;
    ref(FittingRoom) fittingRoom1;
    fittingRoom1 :- new FittingRoom;
    activate new Person("Sam");
    activate new Person("Sally");
    activate new Person("Andy");
    hold(100);
end;

主程序是前綴着模擬器Simulation的有前綴塊。模擬器類可在任何塊上使用,而且模擬器甚至可以嵌套,比如在模擬某人做模擬的時候[33]。時間過程time、等待過程wait、保持過程hold和激活過程ACTIVAT,定義在Simulation類之中。

進程類ProcessSimulation類的嵌套類。激活語句activate,只有處在Simulation類所包含的類的對象之中,或處在其前綴部份是這種對象的有前綴塊之中,才是有效的。激活語句的作用,被定義為得到調用激活過程ACTIVAT的那種效果。

集合類SimsetSimulation類的前綴類,其中定義了三個嵌套類:表示集合整體Head類,表示集合元素Link類,和二者的前綴類鍊表Linkage。首位過程first,定義在Head類中;退出過程out和在等待過程wait中用到的進入過程into,定義在Link類中。

在試衣間類FittingRoom中,為了讓人們排隊等待訪問試衣間,使用了門對象door,它是隊列類即Head類的一個對象。試衣間類定義了兩個過程:要求過程request:如果試衣間中正有人使用,則他必須等待於門隊列之中,即wait(door);當他可以使用試衣間之時,相應的從門隊列中移除自己,即door.first.out。離開過程leave:如果門隊列中有人等待的話,放行其中第一個人,即activate door.first

個人類PersonProcess類的子類,它的行動是瀏覽商店和在試衣間試衣,使用保持過程hold來描述其所用時間,並調用對應的試衣間對象的過程來要求和離開試衣間。正態分布隨機抽籤過程normal,定義在環境類ENVIRONMENT中,它的最後的參數,必須是指定一個偽隨機數串流的一個整數。

主程序是進程類Process的子類MAIN_PROGRAM類的對象。它建立FittingRoom類的實例fittingRoom1對象,接着建立並激活Person類的三個對象,從而將他們三人放置入事件隊列之中,主程序在終止前保持100分鐘的模擬時間。

參見

注釋

延伸閱讀

外部連結

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads