程式設計是最好的邏輯能力訓練方法! - thoughtbot

banq發表於2021-05-07

程式程式設計除錯是最好訓練培養一個人邏輯推理能力的方法,沒有之一。數學推理過於嚴謹,人性化不足,普及性不夠,想透過數學普及提升普通人邏輯能力,容易引起牴觸,數學存在天賦論之說影響其普及;其他辦法如科學實驗與研究成本太高,因此程式設計訓練邏輯能力最有效,比聽一萬遍羅振宇的“邏輯思維”講座更有效。原文點選標題,大意如下:
當古典哲學家研究人類如何思考時,他們發現可以建立有關複雜證明或反駁一個想法的各種方式,這催生了對邏輯推理作為主題的研究。他們確定了一些廣泛的邏輯思考方法。這些可以在除錯時為我們提供有用的框架,因為它為我們提供了一種方法來有條不紊地尋找真相:為什麼會這樣?
古典哲學中沒有明確的推理型別列表。以下是我發現對自己的經歷特別有幫助的一些內容。為了使示例易於訪問和有用,我還對每種型別的定義進行了一些鬆散的介紹。
 

類推推理
這是我最喜歡的方法之一!類推推理是一個三步過程:

  1. 將您的難題轉化為等效的難題(稱為 模擬)。
  2. 解決簡單的問題。
  3. 將簡單的解決方案轉換為針對您的難題的解決方案。

程式設計是最好的邏輯能力訓練方法! - thoughtbot

在類比除錯過程中,一種特別有用的方法是將錯誤減少到 最簡單的形式。在您的上下文中,“最小”的含義可能並不總是很明顯。沒關係。您可以一次慢慢地消除一些複雜性,同時確認該錯誤仍然存​​在。最終,您將剩下一小段程式碼。現在應該更容易找到導致錯誤的原因。對於整個問題,解決方案可能是相同的。
當處理較大系統中的錯誤而不是單個檔案中的錯誤時,這樣做可能會更困難,因為此類錯誤通常是由多個元件之間的互動產生的 。假設您在新增影像處理gem之後注意到了Rails應用程式中的損壞行為。Ruby壞了嗎?您配置不正確嗎?您現有的其他一些程式碼是否與新的gem不相容?
您如何簡化這個問題?一種方法可能是生成全新的空白Rails應用程式並新增可疑的gem。如果問題仍然存在,您現在可以除錯一個小得多的應用程式。找到解決方案後,可以將其移植回您的真實應用程式。
除了簡化現有上下文之外,類比還允許您將錯誤轉移到共享一些相似之處並且可以更好地進行除錯的其他上下文中。
 

排除法
有時,消除錯誤答案比找到正確答案要容易。消除過程屬於歸納推理類別。
在除錯中,這種方法的一種特別有用的形式是二進位制搜尋。這個想法是您進行了一系列實驗來驗證錯誤是否是由特定的程式碼段引起的。設計實驗時,無論結果如何,您都可以消除大約一半的可能性。不斷重複,直到找到錯誤的根源。
儘管名字花哨,但可以很簡單地完成。例如,我正在嘗試查詢特定檔案中發生的錯誤。我可以使用註釋或條件語句來阻止一半檔案執行。我還能重現該錯誤嗎?如果是這樣,那麼我知道該錯誤在活動的另外一半檔案中。如果不是,它必須位於檔案的非活動部分。無論哪種方式,我都消除了一半的可能性,並且更接近於發現錯誤的來源。

程式設計是最好的邏輯能力訓練方法! - thoughtbot

許多程式不只是一個又一個線性的指令集。我們有條件和分支邏輯,導致流看起來更像樹而不是列表。我們仍然可以使用二進位制搜尋方法。與其使用“將列表分成兩半”的思維模型,不如從樹上修剪樹枝方面進行思考。您可以消除的每個分支都縮小了繼續尋找錯誤來源所需的區域。

程式設計是最好的邏輯能力訓練方法! - thoughtbot
二進位制搜尋不僅可以發現空間中的錯誤,還可以及時發現錯誤!git bisect命令使您可以在git歷史上使用相同的方法來有效地找出何時引入了特定的錯誤。
 

演繹推理
人們通常在談論“邏輯”或“推理”時會想到這些。除錯時,我們始終在頭腦中進行此操作。給定一系列起始事實(稱為前提),我們構建了一個邏輯鏈以得出結論。可能看起來像:

  1. 鑑於在驗證唯一性約束時Postgres會引發重複的鍵錯誤
  2. 鑑於唯一性約束違規僅發生在INSERT或UPDATE 語句上
  3. 鑑於唯一的資料庫寫入發生在CreateOrderService 物件中
  4. 因此,錯誤必然在CreateOrderService物件中發生。

我們怎麼知道呢?演繹推理是推理的最數學形式,可以用一種等式表示:x ⇒ y,讀作“如果x然後y”,或“ x表示y”。

duplicate_error ⇒ index_violation
index_volation  ⇒ db_write
db_write        ⇒ create_order_invoked


可以將各種數學定律應用於這些“邏輯方程”。有些人只是感覺常識,而其他人(例如,摩根大通定律) 並不立即直觀。如果您想進一步研究該主題,則需要搜尋的術語是“命題邏輯”。

程式設計是最好的邏輯能力訓練方法! - thoughtbot
這裡特別令人關注的是傳遞屬性。它宣告瞭if.then.then的長鏈,例如x ⇒ y ⇒ z可以簡化為一個if..then x ⇒ z。在上面的示例中,這意味著:

duplicate_error ⇒ create_order_invoked


但是要當心一些陷阱。如果您的任何起始前提是錯誤的,那麼整個事情就會分崩離析。在上面的示例中,如果還有其他檔案也寫入資料庫,那麼我們的結論可能是錯誤的!
一個更微妙的陷阱是,您的直覺可能會導致您誤用某些法律。這種情況的一個特別常見的例子是將⇒這種關係視為雙向有效。僅僅因為索引衝突意味著發生資料庫寫入並不意味著反過來也必然,資料庫寫入並不意味著發生索引衝突。
弄清楚這些錯誤真的很容易。通常,從此類錯誤中恢復的最佳方法是放慢腳步,寫下您的前提 以及得出結論的方式。知道一些命題邏輯符號可能會有所幫助,但是普通的舊散文也可以。
 

矛盾證明
在拉丁語短語中矛盾證明也稱為“ reducio ad absurdum ”,意思是“減少到荒謬”。這是演繹推理的一種變體,但是不是去嘗試證明事實是正確的,而是試圖證明它是相反的,是錯誤的。
該過程通常如下所示:
選擇一個您想證明是正確的假設。現在,假設相反的說法是正確的。使用演繹推理從相反的假設中得出合理的結論。得出一個不可能或荒謬的結論。
目的是得出不可能正確的結論(矛盾)。因為它不能成立,所以您的相反假設不能成立,因此原始假設必須成立。用命題邏輯表示法:

P ⇒ Q
¬Q
∴ ¬P


實際上這是什麼樣的?
假設您在嘗試將錯誤資料從表單儲存到資料庫時遇到錯誤。它可以是很難展現演繹其中的錯誤發生,但可以很容易地顯示在那裡沒有發生,這是以一種迂迴的方式,告訴我們在錯誤發生在哪裡。
您懷疑該錯誤是在資料庫外部發生的。透過矛盾推理將嘗試遵循相反的推理(錯誤發生在資料庫內部),並表明這導致了荒謬。
  1. 鑑於資料,我們試圖儲存與現有資料重複的資料
  2. 鑑於我們的資料庫具有唯一索引
  3. 鑑於將重複資料儲存到資料庫會導致唯一索引異常
  4. 鑑於我們沒有看到唯一的索引異常
  5. 假設錯誤發生在資料庫中(我們的相反結論)
  6. 因此,我們的資料庫不強制執行唯一索引(矛盾)

與演繹邏輯一樣,如果您的前提不正確,那麼您的結論也可能是錯誤的。特別是,“這不可能發生”的假設常常是錯誤的。在上面的示例中,如果發現該特定資料庫表上沒有唯一索引,我們的邏輯將崩潰。糟糕!
 

歸納推理
當我們看一堆具體示例並嘗試推匯出更廣泛的原理時,將使用歸納推理。在除錯中通常是這種情況。我們並非總有一套“真相”可以作為推理依據。相反,我們只有:“在情況X中,發生這種奇怪的行為,但在情況Y中,發生了另一種奇怪的行為”。
有更多的樣本案例可以進行推理特別有用。因此,我們嘗試在本地重現問題。我們更改一些輸入,然後看它如何影響結果。我們做很多筆記。我們甚至可能在生產中累積日誌。然後,我們嘗試檢測模式。
17世紀的科學名稱是“自然哲學”,這並非毫無道理。一旦我們認為我們看到了一些模式,我們就會嘗試證明我們的直覺是錯誤的。為此,我們可以進行建議的修復,然後嘗試手動重現該錯誤。我們還可以新增一些自動化測試用例,以檢查觸發問題的一系列已知方法。如果我們始終無法透過更改觸發該錯誤,則我們的修復很可能是正確的。
請注意,與科學一樣,歸納推理並不能證明 任何東西。相反,它是給定我們可以訪問的方案的最佳解決方案的最佳近似值。可能有一些我們未曾考慮過的極端情況。我們的“解決方案”可能只是掩蓋了症狀,而沒有解決根本問題。最終,我們可能會得到更多表明我們錯了的示例案例。
我在一段程式碼中丟擲了很多不同的資料,然後看它們如何反應。我甚至可以對程式碼本身進行一堆半隨機的更改,然後看看它如何改變結果。我們的目標不是要找到解決方案,而是要生成一系列示例案例,以便我有足夠的資料點來開始觀察模式。
 

謬論謬誤
當我們探索每種型別的推理時,總是存在陷阱,推理會錯誤地將我們引向錯誤的道路。但是,僅僅因為您的推理存在缺陷並不一定意味著您的結論是錯誤的。這就是謬論謬誤。有時候你會很幸運。在其他時候,即使您用來證明其合理性的邏輯是錯誤的,您的直覺也會引導您找到正確的解決方案。
始終驗證您的假設,前提和結論!
 

總結
這裡的推理形式並非彼此排斥。在典型的除錯會話中,您可能希望將它們全部一起使用。實際上,即使閱讀本文,您可能也認為所描述的某些技術來自其他部分的推理方法。
我們直觀地使用所有這些推理方法,並且每天進行除錯時都會使用更多方法。但是,對我們使用的方法有一個清晰的瞭解,可以使我們以更加結構化的方式來尋求解決方案,並且避免四處走動。知道我們正在使用的技術的陷阱也可以使我們保持警惕並避免某些邏輯上的死衚衕。
 
這篇文章是我們正在進行的2021年除錯系列的一部分。

相關文章