假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

NO IMAGE

假設看到了這樣的代碼:

if (a && d || b && c && !d || (!a || !b) && c) {
console.log('pass')
} else {
console.log('fail')
}

你是否一口老血噴在屏幕上?

當然,這段代碼是我參考一個掘金沸點(@隔壁村的李二狗)偽造的,但願你和你的同事都別這麼寫。

能寫出這種與或非,如果不是邏輯不清,那麼估計只有一種可能,需求變更很多次,多人修改後的結果。。

現在的問題是,面對如此代碼,這麼複雜的邏輯,能否優化?

答案是肯定的!

這種問題的探究來自於一門數學:布爾代數或邏輯代數。

說到布爾,我們開發者對此太熟悉了。不就是 true 和 false 嘛。

布爾代數這門課,大學裡應該或多或少都講些吧。因為我是數學專業畢業的,看到這種問題必須記錄一下,也順便複習一下。

為了方便表達,JS中的與或非,我用布爾代數裡面的表示方法(感謝掘金支持latex公式):

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

因此 a && d || b && c && !d || (!a || !b) && c 可以表達為:

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

轉化後優先級清晰多了。

現在的問題是,我們如何化簡這個邏輯表達式。

還好有一些常用結論可供我們使用:

  1. 假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

  2. 假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

  3. 假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

  4. 假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

  5. 假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

前四個公式很好理解。比如假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c,自己或上自己否,當然為 true。

關鍵在於第5個公式沒那麼直觀,可以通過畫圖簡單說明下(其他公式都可以類似推導)。

首先看假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c部分,如圖中綠色區域:

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

再看假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c,如圖中綠色區域:

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

則二者之和為:

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c部分正是圖中粉色區域:

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

因為粉色區域原本就在前二者之和裡面,因此加多少假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c次都是一樣的。

注意最後這個公式核心特點:假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c是以和出現的。

有了這幾個公式作為鋪墊,我們就可以正式推導了(原沸點配圖中有):

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

根據第4條,替換最後一項:

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

注意到前兩項分別有假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c,符合第5條:

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

此時最後兩項,符合第2條:

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

最後兩項,其中一項為假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c,另外一項也包含假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c,滿足第3條:

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

至此化簡完了。真是不可以思議,B沒了!

此時開篇的代碼簡化成了這樣:

if (a && d || c) {
console.log('pass')
} else {
console.log('fail')
}

真清爽。

點擊驗證二者等價性

感謝你看到這裡,希望有所幫助。

另外,在2019年末,立了個flag,數學和動畫將是我今年的核心輸出點。

假如你的同事寫了這樣的代碼:a&&d||b&&c&&!d||(!a||!b)&&c

圖中二維碼是我的唯一微信號,如有朋友想加的,麻煩備註下來源哈,比如“掘金”。

本文完。

相關文章

六年碼農生涯的2019總結:君子坐而論道,少年起而行之

一次kafka消息堆積問題排查

第三屆SEEConf2020演講資料(PPT)

Spring工程多數據源多個事務管理器導致事務失效