🎯 什麼情境該想到我

當你要替一堆複雜的真實世界資訊設計資料表示法,一次想不齊、越想越亂的時候。

「在設計真正的函數時,常常會遇到這樣的任務,要求設計複雜形式資訊的資料表示法。完成這種任務最好的方法是使用一種著名的科學方法:反覆精化。」(p.131)

注意這張卡的對象是資料表示法,不是「功能」或「進度」。書給的理由是:「既然資料表示法在程式設計者的工作中起了主導作用,問題的關鍵就是找出真實世界資訊的精確資料表示法。」(p.132)

⚠️ 別跟 stepwise refinement 搞混。 書講的「反覆精化」精化的是資料表示法;Wirth 那套「逐步求精」是把一個抽象步驟逐層展開成實作。名字像,做的事不同。要「先做能跑的最小版本再長肉」,那是 工具-曳光彈開發,不是這張。


⚙️ 怎麼用

先看書借來的類比:科學家怎麼建模型

「科學家們使用數學來表示真實世界,他們努力所得的結果稱為模型。科學家們會使用多種方法測試模型,特別是使用模型來預測世界的屬性。如果模型真的描述了真實世界的本質,那麼這樣作出的預言就是準確的;否則,在預言和實際結果之間就會有矛盾。」(p.131)

書的例子:物理學家**「可能用一個點來表示噴射機,然後使用牛頓方程預測它的運動軌跡為一條直線。後來,如果需要求飛機所受的摩擦力,該物理學家可能會在模型中加上飛機的輪廓線,用來表示其外形」——「一般來說,科學家會改進模型,重新測試它的有效性,直至模型充分準確為止。」**(p.132)

「程式設計者或者計算機科學家應該進行和科學家一樣的行動。」(p.132)

方法本體

「在複雜情形下,要做到這一點的最好方法就是反覆設計表示法,從問題的基本元素開始,在充分理解當前模型後,再添加問題的更多特徵。」(p.132)

拆成可執行的步驟:

  1. 先做資料分析,決定看哪裡、忽略什麼。

    「需要做的第一個決定是,該把注意力集中在哪裡,又該忽略什麼東西。」(p.133)

    書處理檔案系統時,明講**「就我們的用途而言,檔案就像是表;我們忽略為什麼計算要永久地存儲檔案,以及它是怎樣永久存儲檔案的」**(p.132)。想清楚要忽略什麼,跟想清楚要表示什麼一樣重要。

  2. 第一個模型只放最基本的元素,能多粗就多粗。 書的模型一把檔案當成**「一個代表檔名的符號」,目錄當成「包含檔案和目錄的表」**(p.133)。

  3. 充分理解當前模型再往下走——包括拿它去寫函式。模型一寫完就發現**「目錄類型就是第 14.3 節中的網頁類型。因此,我們可以重用網頁處理函數的模板來處理目錄樹」**(p.133),並配了 how-many 之類的習題把模型用過一遍。

  4. 找出當前模型「藏起來」的東西,加進下一版。 這是往下一版走的判準,書寫得很直接:

    「雖然我們很熟悉第一個資料定義,而且它用起來也很方便,但是它隱藏了目錄的本質。具體說來,它隱藏了這樣一個事實,即目錄並不只是檔案和目錄的集合,它還有一些有趣的屬性。」(p.133)

    於是模型二引入結構體 (define-struct dir (name content))「它表明目錄有名字,有內容;現在,如果需要的話,我們還可以加上其他的屬性」(p.133)。

  5. 一次只加一類特徵,加完就把資料定義重寫一遍。 模型二只補上目錄的屬性;模型三才輪到檔案:「第二個資料定義改進了第一個資料定義,引入了目錄的屬性。檔案也有屬性。要建立檔案屬性的模型,我們還是一樣處理。」(p.134)模型三於是有了 (define-struct file (name size content)),並把 dir 的 content 欄位拆成檔案的表與子目錄的表(p.134-135)。

  6. 每一版都重新檢查資料定義之間的引用關係,該同時引入的就同時引入。

    「因為 dir 的資料定義引用了 LOFD 的定義,而 LOFD 的定義又反過來引用了 dir 的資料定義,所以它們是相互引用的定義,必須同時被引入。」(p.134)

  7. 停在「抓住本質」的那一版,然後用它開發函式。

    「這第三個目錄層次的(資料表示法)模型抓住了檔案系統的本質,至少是用戶一般可以觀察到的本質。不過,它有兩個結構體定義,四個資料定義,比第一個模型複雜的多。但是,從第一個模型的簡單表示法開始,通過一步一步地改進,我們理解了如何處理這種複雜類型的組織。」(p.135)

    接下來的工作是**「使用第 15.2 節中的設計訣竅來開發處理這個資料定義集合的函數,不然的話,我們就完全沒有辦法來理解這種定義」**(p.135)。書第 16.3 節就是拿最精確的模型去寫 how-manydu-dirfind? 等函式(p.135-136)。

三個模型的演進(書第 16 章的檔案系統例子)

版本表示什麼加了什麼 / 為什麼要往下一版
模型一(p.133)file 是符號;dir 是「empty/(cons f d)/(cons d1 d2)」最原始:檔案=基本實體,目錄=容器。優點是「就是第 14.3 節的網頁類型」,模板可直接重用
模型二(p.133-134)(define-struct dir (name content));dir 與 LOFD 相互引用因為模型一「隱藏了目錄的本質」——目錄有屬性。相互引用的定義必須同時引入
模型三(p.134-135)(define-struct file (name size content))(define-struct dir (name dirs files))、list-of-files、list-of-directories檔案也有屬性;dir 的 content 再拆成檔案表與子目錄表。兩個結構體定義、四個資料定義

這不是第 16 章才發明的一次性技巧

「本書已經在許多補充練習中使用了反覆精化。例如,移動圖形的練習從簡單的圓和矩形開始;後來,開發了移動整個圖形的程式。類似地,我們先以單詞和嵌入網頁表的形式引入了網頁;在第 15.3 節中,我們改進了嵌入網頁的表示法。不管怎樣說,對於所有這些練習,改進都是建立在表示法上的。」(p.132)

最後一句是這張卡的判準:精化的對象是表示法


🧪 我實際套用的紀錄

  • 2026-07-14:(待填)

⚠️ 注意

  • 精化的對象是資料表示法,不是「功能」。 書自己劃了這條線:「對於所有這些練習,改進都是建立在表示法上的」(p.132)。本章沒有談「先做能跑的最小版本再逐個加功能」這類交付順序的問題。
  • 越精確不等於越好用。 模型三「比第一個模型複雜的多」(p.135);模型一雖然粗,但因為型別剛好等於既有的網頁類型,模板可以整套重用(p.133)。精化到夠用就停。
  • 要決定忽略什麼。 書把「檔案為什麼能永久保存、怎麼永久保存」整個排除在模型外(p.132);不排除就做不出第一版。
  • 精化只給你資料定義,函式還是要照設計訣竅走。 第三個模型做完,書的下一句話是「現在,我們的任務是,使用第 15.2 節中的設計訣竅來開發處理這個資料定義集合的函數」(p.135)。
  • 模型要拿去用才知道對不對。 每個模型後面書都掛了習題:把圖 16.1 的檔案系統轉成該模型的 Scheme 表示、用該模型開發 how-many(習題 16.2.1、16.2.2、16.2.4、16.2.5、16.3.1、16.3.2)。對應到科學家的類比就是「使用模型來預測世界的屬性」(p.131)。

🔗 相關工具

  • 工具-設計訣竅 —— 反覆精化產出資料定義之後,函式仍然照設計訣竅開發(p.135)。
  • 工具-資料驅動的模板推導 —— 精化的產物是資料定義,而模板是從資料定義推出來的:第 16 章的模型二一定案,書就說「要設計一個處理 dir 的函數,我們必須並行地開發 dir 處理函數和 LOFD 處理函數的模板」(p.134)。
  • 回連 程式設計方法