把一個集合切成幾塊來討論時,「一部分」「另一半」「那一側」「這部分」只說了有一刀,沒說刀落在哪裡、每一塊是什麼。讀者要自己從例子裡歸納出切分的依據,歸納出來的還未必是作者的那一個。修法跟重構程式時抽變數是同一個動作:先寫出切分的依據(為什麼這一塊做得到、那一塊做不到),再替每一塊取名字,之後只用名字指它。名字依序從三處找:業界已有的術語、文章裡現成的案例名、最後才是一個描述內容的短語。

失效的形態

一篇 SQL 教材討論「答案對不對」這一層能不能交給機器判斷,原文三段的開頭是:

交不出那條判定,而這裡仍有一部分不必等人來判。

有一部分判斷標準交得出去。

交不出去的那一半仍然有一個可以機械化的做法。

同一個集合被切了三次,三塊都沒有名字,而三刀的依據各不相同,分散在段落中段的一句話裡(「交得出去的界線落在判斷需要什麼」只解釋了約束那一刀)。讀到「交不出去的那一半」時,讀者要先決定它指的是「這裡仍有一部分」切剩的、還是「有一部分判斷標準交得出去」切剩的。

同一節的最後一段是反例——它做對了:

預期值從哪裡取得叫 test oracle……「寫斷言的人與寫實作的人共用同一份誤解」叫 test provenance;而「造不出分岔的資料」在 Mutation Testing 裡叫 equivalent mutant。

三個概念都已經在別處定義過、有公認的名字,所以一句話就能引用三次,讀者不必重建任何定義。

改寫之後,那一節開頭先寫依據:一項判斷能不能交給機器,看判斷時要讀的資訊機器在那一刻讀不讀得到——引擎收到查詢時讀得到查詢文字與表結構,資料庫寫入時讀得到那一筆資料與它指向的列,「要問的是什麼」只有提問的人知道。依據寫出來之後三塊各有名字:只讀查詢文字與表結構的是靜態分析,只讀寫入那一筆的是約束,要讀「要問的是什麼」的只能記成一筆反例資料(counterexample)。之後每一段都用名字開頭。

取名這一步會逼出內容

找業界名字不只是換標籤。「這裡仍有一部分不必等人來判」要改叫靜態分析,就得說出靜態分析在這個例子上實際判得出什麼——追下去找到的是最佳化器本身就做這個判斷:WHERE 評價.星等 <> 5 讓 LEFT JOIN 失效,PostgreSQL 16 的 EXPLAIN 對那段查詢印 Hash Join、對正解印 Hash Left Join,MySQL 8 把它改寫成內連接。原文的「那個 LEFT 是死的」是一句斷言,取名之後它變成讀者可以自己跑一次驗證的現象。

「分岔的資料」換成「反例資料」也一樣:反例在邏輯裡的意思是「兩段查詢在它上面答案不同,就證明兩者不等價」,這個定義順手把最後一段的 equivalent mutant 接上了——造不出反例資料,就是兩段查詢等價。自創的「分岔的資料」接不上任何既有概念。

同一篇第二輪把成因分類與反例資料的狀態也命名之後,又對出兩處:成因那一節寫「問錯了問題,換引擎、加約束、跑靜態分析都碰不到」,而後面一節寫靜態分析判得出「不等於五」——兩類有了名字,才看得出「不等於五」同時屬於「誤解語意模型」,靜態分析判出的是後者;「這幾種狀態」列了四種資料狀態並說三種錯法都發生在上面,而「漏掉星等」的反例(某張訂單的評價全部不是五星)不在清單裡、清單裡的「指不到任何一列的編號」三種錯法都用不到。改叫等價類劃分(equivalence partitioning)並把資料實際寫出來跑過一次,清單才對得上。

為什麼命名的既有規則沒擋住

#363 要求正文裡的對象用內容命名,掃的是序數、計數與字母代號(「第一支」「三步」「A1」)。那些字面上就是索引,一眼看得出來。「一部分」「另一半」不是索引,它們是切分動作本身:作者寫下的時候手上有切法,所以對他來說「那一半」跟「約束那一類」一樣清楚,而 #363 的變數名稱測試問的是「這個名字能不能當變數」,「那一半」根本沒有被當成名字寫下,測試不觸發。

跟 #366 的關係是同一個成因的兩個尺度:#366 是一個名詞缺了所屬(「誰的輸出」),這一張是一整個集合缺了切分依據與成員名——「一部分」要求的所屬是「什麼的一部分、按什麼切出來的哪一部分」,句內兩樣都沒有。

偵測與它的射程

字詞層掃得到候選:一部分|另一半|這一半|那一半|這部分|那部分|另一部分|那一側|這一側|另一側|另一邊。在 SQL 分類 24 篇與知識卡上跑一次,程式碼區塊外 68 處命中,判定要改的 20 處。

不用改的那些,所屬寫在同一句:「子表這一側是填進一個指不到的值,父表那一側是把還被指著的那一列刪掉」「反連接留下的是另一半——左邊那些在右邊配不到任何對象的列」。要改的那些分兩種:

  • 半邊沒有名字:「補決勝鍵只解決了並列那一半」(另一半是翻頁途中的寫入,要到另一篇才看得到)、「規則沒有規定的那一半」「列數那一半在 1.6」。改成直接寫出那一塊:「治得好並列造成的重複與遺漏、治不好翻頁途中有人寫入造成的那一種」。
  • 「一側」當譬喻:「在條件這一側還有路」「語言這一側發生什麼」「代價那一側它是逐列執行的」。這裡沒有被切開的集合,「一側」只是換了個說法的「就……而言」,改成直接寫主詞(「改寫條件本身就能讓它走索引」)。

grep 分不出兩者與合規句,命中之後逐句問:讀者只讀這一句,說不說得出這一塊是什麼、另一塊是什麼。另外,切分依據沒寫出來這件事沒有字詞可掃——三刀之中真正缺的是依據,而依據的缺席沒有痕跡。寫到「一部分」的當下先回答「按什麼切的」,再把答案寫在第一次切的位置。

修法

  • 先寫依據:切開一個集合的第一句寫出「按什麼切」,而且是一個可以逐項判定的標準(「判斷時要讀什麼」),不是結果的描述(「有的交得出去」)。
  • 再命名:業界有術語就用術語(靜態分析、約束、counterexample),並寫一句它在本文例子上的樣子;沒有術語就用文章裡的案例名(「不等於五的錯法」);兩者都沒有才用描述短語。
  • 之後只用名字:段落首句用名字開頭(「靜態分析只讀查詢的文字與表的結構」),不再用「這一部分」「另一半」回指。
  • 審查端列為必修:教材逐段審查把「一個被反覆談論的概念沒有被界定與命名就被引用」列為必修,逐個概念點名檢查。審查者手上有作者的切法,讀到「那一半」解得開,所以靠讀起來順不順判斷永遠通過。同一條也涵蓋沒有字詞可掃的那一種:同一個概念每次換一個描述重講一次,讀者分不出幾次講的是不是同一件事——那是程式裡重複的運算式,一樣要抽成變數。
  • 連到切分的句子一起改:別篇引用這個切分時用的也是「一部分交得出去」,要一併換成名字,否則讀者在兩篇之間拿到兩套說法。

命名由句子結構完成,取名的動作不寫進正文

一篇服務端監控的教材照這張卡先界定再命名,寫成:「一個觀察者的盲區有兩種,各取一個名字,下文都用這兩個名字。問題盲區是……位置盲區是……」。中間那個子句描述的是作者正在做的事(取名、約定下文怎麼用),刪掉之後讀者什麼都沒少:粗體與緊跟的定義已經完成命名,之後一律用名字是寫的人自己的紀律,不必向讀者宣告。

命名交給句子結構:

  • 成員只有兩三個:一句寫完分類與成員的名字,下一句起逐一定義。「一個觀察者的盲區分成問題盲區與位置盲區。問題盲區是……」
  • 成員更多:用清單列出名字,每一項可以附一句判別依據,下一段起逐一解釋。清單用編號只是版面,正文引用時仍然用名字。

判讀用刪除測試:刪掉這個子句,讀者少了什麼。 少了名字或它的所指就留下——「下文把主機、容器、資料庫、快取與入口這些組成服務的單位稱為元件」刪掉之後讀者不知道元件指什麼;什麼都沒少的刪掉——「各取一個名字」「下文都用這兩個名字」「以下逐一說明」。

這個形態是執行本卡規則的副作用:規則描述的是寫的人要做的動作(先界定、再命名),寫的人把動作本身寫進了正文。它和 #285 的文章自指是同一類,讀者讀的是主題,不是文章怎麼安排自己。

全站掃過一次之後:模糊的切分會藏住錯的論斷

SQL 以外的分類另有 550 處候選,分十批交給篩選者(判定只看目標句與前一句、不改要附逐字依據),判定要改 89 處,主 session 重讀依據薄弱的不改項後再改 7 處,約六分之一。不改的多數是組成句「X 是 Y 的一部分」——部分與整體都寫在句內,沒有切開任何集合;這一類可以直接豁免。

篩選者回報了規則原先沒寫到的三種形態:

  • 每一塊有名字、被切開的整體沒有:「purgeable 是同一條線的另一半」「環境那一半的論證」——讀者知道這一半叫什麼,不知道它是什麼的一半。四個批次各自遇到,判定不一致,所以要寫進規則:整體也要在這一句或前一句具名。
  • 裸「這部分」回指,而前面沒有切分:「或把這部分 relationalize」前一句有兩個名詞都可能是先行詞;「必要時用 chvt 切到別的 VT,這部分見……」跟在一長串操作之後。這是指涉問題,修法相同:寫出那一塊的名字。
  • 同一組東西混用幾個詞、或同一個詞指兩塊:「單邊/那一腳/那一邊」指同一組兩腳;「只算資本這一邊(……談的是資本的另一半)」讓「資本」同時指資金與人力資本。命名時整組一起定,一個名字只指一塊。

最值得記的是另一件事:有兩處模糊的切分底下是錯的論斷。 存活者偏誤那一篇的通則寫「往被高估那一側走還是贏的結論仍然成立,靠近基準的指控型結論不成立」,而前一行才寫明觀察值是下界時「低於基準」存活——通則只在上界時成立。保證金模式那張卡寫「這個預設在猜錯時付出的是資金效率,猜對的另一邊付出的是同時被清算」,「另一邊」其實是反向的預設猜錯時,句子把邏輯寫反了。兩句讀起來都通順,因為「那一側」「另一邊」不必決定指的是什麼;替它們命名的時候才發現對不上。

反方向也發生了一次:篩選者替「purgeable 是同一條線的另一半」取名,寫成「與本地快照並列的另一個根因」,而同一段寫明 purgeable 主要由快照構成,兩者不並列。命名會逼作者決定所指,而決定錯了就把一個錯誤寫進文章,所以替換文字裡新出現的關係(並列、包含、因果)要回原文核對。

論述基礎與限制

依據是一次改寫:一篇文章的一節加上 SQL 分類的全掃(68 處候選、20 處改動,判定由主 session 做),以及其餘分類的全掃(550 處候選、96 處改動,篩選者判定、主 session 抽查與核對)。「取名會逼出內容」觀察到四例(最佳化器的外連接化簡、反例接上 equivalent mutant、兩處寫反的論斷),不代表每一次命名都會找到新材料。

跟其他抽象層原則的關係

判讀徵兆

  • 同一個主題被「一部分」「另一半」「那一側」切了兩次以上,而切分的依據要讀完整段才歸納得出來。
  • 段落首句用「這一部分」「另一半」回指上一段,而上一段沒有替它命名。
  • 自創了一個描述性名稱(「分岔的資料」),而業界對同一件事已有術語。
  • 命名的句子另有一個子句宣告取名或約定下文用法(「各取一個名字,下文都用這兩個名字」),刪掉之後讀者什麼都沒少。

Case

2026-09-29,SQL 教材分類的 SQL.13。使用者指出「交得出去的一部分、交不出去的另一半」是不好的例子,同一節的 test oracle / test provenance / equivalent mutant 那一句是好的例子:「當我們先說明界定好一個理論或者一個概念,我們就可以直接引用他的名字」;要解釋為什麼這個情境做得到、另一個做不到,然後給一個明確的命名——業界的說法,或直接舉一個案例、用案例的名字——「這也是當我們寫程式碼的時候有時候應該先嘗試抽變數的概念,只是這個變數在文章中他可以是一個 case,或者一個理論」。