1967 - Simula 67(物件導向程式設計的誕生)#
案件摘要#
1967 年,挪威計算中心(Norwegian Computing Center, NCC)的 Ole-Johan Dahl(1931–2002) 與 Kristen Nygaard(1926–2002) 發表 Simula 67——
他們為了模擬港口與工廠的作業流程,在 ALGOL 60 的骨架上加入了三個革命性的概念: - 類別(class)與物件(object):資料與操作資料的程式碼綁在一起; - 封裝(encapsulation)與繼承(inheritance):子類別複用並擴充父類別; - 协程(coroutine)與虛擬函式(virtual):多個物件交替執行、行為可被覆寫。
三十餘年後,C++、Java、Python——一切現代物件導向語言都是 Simula 的後裔—— 2001 年,兩人共同獲得圖靈獎;隔年(2002),兩人相繼逝世。
前因 -- 為什麼會有這個案子#
- ALGOL 60 的遺產(1958–1960): 國際委員會設計的 ALGOL 60 是最優雅的程式語言: 區塊結構(begin ... end)、遞迴程序、BNF 文法(見
1968-克努特演算法藝術.md)。 但 ALGOL 60 是為數值計算而生——它沒有:
- 挪威計算中心的現實需求(1961–1965): Nygaard 在 NCC 負責作業研究(operations research)顧問工作: 為港口、工廠、排隊系統建立模擬模型。 他發現:傳統的數學公式無法描述成千上萬個互相作用的「個體」——
每個「船」有自己的狀態(到達、等待、卸貨、離開)—— 這正是「物件」的原型。Dahl 隨後加入,兩人先做出 Simula I(1964), 再於 1965–1967 年重新設計出通用的 Simula 67。 - 垃圾回收的先例(1958 LISP): 物件在執行期動態誕生、動態消亡—— 誰來釋放記憶體? McCarthy 的 LISP(1958) 已示範:
Dahl 在 Simula 中實作了類似的自動記憶體管理—— 這條血脈後來流入 Java、Python、C#(見 1958-麥卡錫LISP.md)。 - 結構化程式設計的浪潮(1968): 就在 Simula 67 問世隔年,Dijkstra 發表 goto 有害論(見 1968-結構化程式設計.md)——
這兩條路線在 1980 年代合流——成為現代軟體工程的兩大支柱。
線索與推理 -- 數學式、程式、理論#
第一條線索:類別與物件——資料與程式碼的合體#
Simula 的 class 是一個藍圖:
物件(object)是 class 的實例(instance):
封裝:物件的內部狀態只能透過其程序存取——
這看似簡單,卻是程式設計思維的哥白尼革命: 程式的單位不再是「程序」(先有動作、再找資料), 而是「物件」(先有資料、動作跟著資料走)。
第二條線索:繼承與虛擬函式——複用與多型#
繼承(inheritance):子類別自動擁有父類別的全部成員,並可新增或覆寫:
虛擬函式(virtual):父類別宣告一個「可被覆寫」的程序—— 呼叫時依物件的實際類別(而非變數的宣告型別)分派:
這是 C++ 的 virtual、Java 的 @Override、Python 的方法覆寫的共同祖先。
第三條線索:协程與離散事件模擬——Simula 的出生理由#
协程(coroutine):多個程序交替執行、彼此讓位(resume/detach)—— 每個模擬物件是一條协程,在模擬時間中輪流「活著」。 離散事件模擬(discrete-event simulation)的數學骨架: - 模擬時鐘只在事件發生時推進:
- 事件佇列(event queue)按時間排序(優先佇列,heap 實現):
- 每取出一個事件,執行它、可能產生新事件(如顧客到達產生下一個到達):
這就是 Simula 內建的 Simulation class—— 也是今天一切模擬軟體(排隊、物流、遊戲引擎)的核心迴圈。
Python:類別繼承 + 離散事件模擬(單一服務台佇列)#
import heapq
class Event:
"""事件:time = 發生時刻(heapq 需要可比較的 key)"""
def __init__(self, time, kind, cust):
self.time, self.kind, self.cust = time, kind, cust
def __lt__(self, other): # 運算子覆寫:多型的 Python 味道
return self.time < other.time
class Customer:
"""顧客:記錄到達與離開"""
def __init__(self, name):
self.name, self.arrive, self.leave = name, None, None
class Simulator:
"""模擬器:事件佇列(heap)+ 模擬時鐘"""
def __init__(self):
self.queue, self.clock = [], 0.0
def schedule(self, time, kind, cust):
heapq.heappush(self.queue, Event(time, kind, cust))
def run(self):
while self.queue:
ev = heapq.heappop(self.queue) # 取最早的事件
self.clock = ev.time # 時鐘跳躍到事件時刻
self.handle(ev)
def handle(self, ev):
raise NotImplementedError # 虛擬函式:留給子類別覆寫
class SingleServerQueue(Simulator):
"""單一服務台佇列:繼承 Simulator,覆寫 handle"""
def __init__(self, lam, mu, horizon):
super().__init__() # 呼叫父類別初始化(繼承)
self.mu, self.horizon = mu, horizon
self.server_busy, self.waits = False, []
first = self.next_arrival(lam, 0.0)
self.c = Customer("C1")
self.schedule(first, "arrive", self.c)
self.lam = lam
def next_arrival(self, lam, t): # 指數到達間隔的均值近似
return t + 1.0 / lam
def handle(self, ev):
if ev.kind == "arrive":
if not self.server_busy:
self.server_busy = True
self.schedule(ev.time + 1.0 / self.mu, "depart", ev.cust)
else:
self.waits.append(ev.time) # 簡記:排隊者到達時刻
if ev.time < self.horizon:
nc = Customer(f"C{len(self.waits) + len(self.queue) + 2}")
nc.arrive = ev.time + 1.0 / self.lam
self.schedule(nc.arrive, "arrive", nc)
elif ev.kind == "depart":
ev.cust.leave = ev.time
self.server_busy = False
sim = SingleServerQueue(lam=1.0, mu=1.5, horizon=20.0)
sim.run()
done = [c for c in [sim.c] if c.leave is not None]
print("單一服務台佇列模擬(Simula 67 精神)")
print(f" 完成服務的顧客數:{len(done)}")
print(f" 模擬結束時鐘:{sim.clock:.2f}")
print(f" 事件佇列依時間排序執行:heapq(優先佇列)")
單一服務台佇列模擬(Simula 67 精神)
完成服務的顧客數:1
模擬結束時鐘:20.67
事件佇列依時間排序執行:heapq(優先佇列)
結案 -- 後果與影響#
- 物件導向的三大支柱(1967):
Dahl 與 Nygaard 在 ALGOL 60 的區塊結構上,種下了現代軟體的基因:
- 後裔譜系:
- Smalltalk(1972/1980,Alan Kay,2003 圖靈獎): Kay 承認 Simula 是 Smalltalk 的直接靈感——把「訊息傳遞」推向極致, 一切皆物件;
- C++(1983,Bjarne Stroustrup): 名字直白——「帶類別的 C」最初就叫 C with Classes, virtual 函式直接取自 Simula;
- Java(1995)、Python(1991)、C#(2000): 一切現代物件導向語言都是 Simula 的後裔—— class、繼承、方法覆寫、垃圾回收,全套血統。
- 離散事件模擬的標準(1967 → 今日):
今日的排隊論軟體、物流系統、遊戲引擎(如事件驅動架構)、 甚至離散事件網路模擬器,用的都是同一個迴圈。 - 圖靈獎(2001)與雙星的殞落(2002): ACM 於 2001 年將圖靈獎授予 Dahl 與 Nygaard—— 「物件導向程式設計的誕生來自他們的研究」。 悲喜交集的是:得獎隔年,兩人相隔一個月先後逝世(Dahl 2002 年 6 月、Nygaard 2002 年 8 月)——
他們生前都看到了自己的思想征服整個軟體世界。 - 歷史定位:Dahl 與 Nygaard 是軟體世界的「孟德爾」—— 他們沒有設計最快的語言,卻找到了軟體的遺傳法則:
今天你寫的每一個 class,都是 1967 年挪威計算中心的一場模擬。
關鍵人物與文獻#
- O.-J. Dahl、K. Nygaard:SIMULA—An ALGOL-Based Simulation Language(1967,CACM);Simula I(1964);2001 圖靈獎。
- K. Nygaard:作業研究顧問——港口與工廠模擬的需求起點。
- J. McCarthy:LISP(1958)——垃圾回收的先例(見
1958-麥卡錫LISP.md)。 - A. Kay:Smalltalk(1972/1980)——Simula 的直接繼承者,2003 圖靈獎。
- B. Stroustrup:C++(1983)——C with Classes,virtual 取自 Simula。
- 相關案件:
1958-麥卡錫LISP.md、1968-結構化程式設計.md、1968-克努特演算法藝術.md。