[譯]優秀的巢狀三元表示式(軟體編寫)(第十四部分)

吳曉軍發表於2018-02-09

[譯]優秀的巢狀三元表示式(軟體編寫)(第十四部分)

(譯註:該圖是用 PS 將煙霧處理成方塊狀後得到的效果,參見 flickr。)

這是 “軟體編寫” 系列文章的第十四部分,該系列主要闡述如何在 JavaScript ES6+ 中從零開始學習函數語言程式設計和組合化軟體(compositional software)技術(譯註:關於軟體可組合性的概念,參見維基百科

< 上一篇 | << 返回第一篇

過去的經驗會讓你相信,巢狀三元表示式是不可讀的,應當儘量避免。

經驗有時候是愚蠢的。

真相其實是,三元表示式通常比 if 語句更加簡單。人們不相信的原因有兩個:

  1. 他們更熟悉 if 語句。熟悉帶來的偏見可能會讓我們相信一些並不正確的事物,即便我們為真相提供了佐證。
  2. 人們嘗試像使用 if 語句那樣去使用三元表示式。這樣的程式碼是不能工作的,因為三元表示式是表示式(expression),而非語句(statement)

在我們深入細節之前,先為三元表示式做一個定義:

一個三元表示式是一個進行求值的條件表示式。它由一個條件判斷,一個真值子句(truthy value,當條件為真時返回的值)和一個假值子句(falsy clause,當條件為假時返回的值)構成。

它們就像下面這樣:

(conditional)
  ? truthyClause
  : falsyClause
複製程式碼

表示式 vs 語句

一些程式語言(包括 Smalltalk、Haskell 以及大多數函數語言程式設計語言)都沒有 if 語句。取而代之的是,if 表示式。

一個 if 表示式就是進行求值的條件表示式。它由一個條件判斷,一個真值子句(truthy value,當條件為真時返回的值)和一個假值子句(falsy clause,當條件為假時返回的值)構成。

這個定義看起來是不是很熟悉?大多數函數語言程式設計語言都使用三元表示式來表示 if 關鍵字。這是為什麼呢?

一個表示式是一個求取單值的程式碼塊。

一條語句則是一個不一定進行求值的程式碼段。在 JavaScript 中,一個 if 語句不會進行 求值。為了讓 JavaScript 中的 if 語句有用,就必須引起一個副作用(side-effect)或者是在 if 語句包裹的程式碼塊中返回一個值

在函數語言程式設計中,我們試圖避免可變性以及其他的副作用。由於 JavaScript 中的 if 先天會帶來可變性和副作用,所以包括我在內的一些函數語言程式設計的擁躉會使用三元表示式來替換它。

三元表示式的思維模式與 if 語句有所不同,但如果你嘗試實踐幾周,你也會自然而然的轉到三元表示式。這可不只是因為它縮小了程式碼量,另一些優勢你也將在後文中看到。

熟悉帶來的偏見

我最常聽到的關於三元表示式的抱怨就是 “難於閱讀”。讓我們通過一些程式碼範例來粉碎謠言:

const withIf = ({
  conditionA, conditionB
}) => {
  if (conditionA) {
    if (conditionB) {
      return valueA;
    }
    return valueB;
  }
  return valueC;
};
複製程式碼

注意到,這個版本中,通過巢狀的條件和可見的括號來分離真值和假值,這讓真假值看起來失去了聯絡。這只是一個很簡單的邏輯,但卻需要花力氣理解。

讓我們再看看同樣的邏輯,用三元表示式是怎麼完成的:

const withTernary = ({
  conditionA, conditionB
}) => (
  (!conditionA)
    ? valueC
    : (conditionB)
    ? valueA
    : valueB
);
複製程式碼

這兒一些值得分享和討論的點:

菊鏈法 vs 巢狀

譯註:菊鏈法(Daisy Chaining),原意指多個裝置按序或者圍城一環進行連線。

首先,我們已經將巢狀鋪平了。“巢狀的” 三元表示式有點用詞不當,因為三元表示式很容易通過一條直線進行撰寫,你完全不需要使用不同的縮排來巢狀它們。它們容易按照直線的順序,自頂向下地進行閱讀,一旦滿足了某個真值或者假值就會立即返回。

如果你正確的書寫了三元表示式,也就不需要解析任何的巢狀。沿著一條直線走,很難迷路。

我們應該稱其為 “鏈式三元表示式” 而不是 “巢狀三元表示式”。

還有一點我想指出的就是,為了簡化直線連結,我對順序稍作了更改:如果你到達了三元表示式末尾,並且發現你需要寫兩條冒號子句(:):

// 譯者補充這種情況
const withTernary = ({
  conditionA, conditionB
}) => (
  conditionA
    ? conditionB
    ? valueA
    : valueB
    : valueC
)
複製程式碼

將最後一條子句提到鏈頭,並且反轉第一個條件判斷邏輯來簡化三元表示式的解析。現在,不再有任何困惑了!

值得注意的是,我們可以使用同樣的手段來簡化 if 語句:

const withIf = ({
  conditionA, conditionB
}) => {
  if (!conditionA) return valueC;
  if (conditionB) {
    return valueA;
  }
  return valueB;
};
複製程式碼

這好很多了,但是 conditionB 的關聯子句被打破仍然是可見的,這還是會造成困惑。在維護程式碼期間,我已經看到過類似問題引起了邏輯上的 bug。即便邏輯被打平,這個版本的程式碼相較於三元表示式版本,還是稍顯混亂。

語法混亂

if 版本的程式碼含有許多噪聲:if 關鍵字 vs ?,使用 return 來強制語句返回一個值、額外的分號、額外的括號等等。 不同於本文的例子,大多數 if 語句也改變了外部狀態,這不僅增多了程式碼,還提高了程式碼複雜度。

這些額外程式碼帶來的負面影響是我不喜歡用 if 的重要原因之一。在此之前,我已經討論過,但在每個開發者都瞭然於胸前,我還是願意不厭其煩地再嘮叨一下:

工作記憶

平均下來,人類大腦只有一小部分共享資源提供給對於離散的儲存在工作記憶中的量子,並且每一個變數潛移默化地消費這些量子。當你新增更多的變數,你就會喪失對這些變數的含義的精確記憶能力。典型的,工作記憶模型涉及 4-7 個離散兩字。超過這個數,錯誤率就會陡增。

與三元表示式相反,我們不得不將可變性和副作用融入 if 語句中,這通常會造成新增一些本不需要的變數進去。

訊雜比

簡練的程式碼也會提高你程式碼的訊雜比。這就像在聽收音機 —— 如果收音機沒有正確的調到某個頻道,你就會聽到許多干擾噪聲,因此很難聽到音樂。當你正確的調到某個頻道,噪聲就會遠離,你聽到的將是強烈的音樂訊號。

程式碼也類似。表示式越精簡,則越容易理解。一些程式碼給了我們有用的資訊,一些則讓我們雲裡霧裡。如果你可以在不丟失所要傳達的資訊的前提下,縮減了程式碼量,你將會讓程式碼更易於解析,也讓其他的開發者更容易理解當中的意思。

藏匿 Bug 的表面積

看一眼函式的前前後後。這就好像函式進行了節食,減去了成噸的體重。這是非常重要的,因為額外的程式碼就意味著更大的藏匿 Bug 的表面積,也就意味著更多的 bug。

更少的程式碼 = 更小的藏匿 bug 的表面積 = 更少的 bug。

副作用與共享的可變狀態

許多 if 語句不只進行了求值。它們也造成了副作用,或是改變了狀態,倘若你想要知道 if 語句的完整影響,就需要知道 if 語句中的副作用的影響,對共享狀態所有的變更歷史等等。

將你自己限制到返回一個值,能強制你遵循這個原則:切斷依賴將以讓你的程式更易於理解、除錯、重構以及維護。

這確實就是三元表示式中我最喜歡的益處:

使用三元表示式將讓你成為更好的開發者。

結論

由於所有的三元表示式都易於使用一個直線來自頂向下地分配,因此,稱其為 “巢狀的三元表示式” 有點用詞不當。取而代之的是,我們稱其為 “鏈式三元表示式”。

相較於 if 語句,鏈式三元表示式有若干的優勢:

  • 它總是易於通過一條直線進行自頂向下的閱讀和書寫。只要你能沿著直線走,就能讀懂鏈式三元表示式。
  • 三元表示式減少了語法混亂。更少的程式碼 = 更小的藏匿 bug 的表面積 = 更少的 bug。
  • 三元表示式不需要臨時變數,這減少了工作記憶的負荷。
  • 三元表示式有更好的訊雜比。
  • if 語句鼓勵副作用和可變性。三元表示式則鼓勵純程式碼。
  • 純程式碼將我們的表示式和函式解耦,因此也將我們訓練為更好的開發者。

在 EricElliottJS.com 上可以瞭解到更多

視訊課程和函數語言程式設計已經為 EricElliottJS.com 的網站成員準備好了。如果你還不是當中的一員,現在就註冊吧

[譯]優秀的巢狀三元表示式(軟體編寫)(第十四部分)


Eric Elliott“編寫 JavaScript 應用” (O’Reilly) 以及 “跟著 Eric Elliott 學 Javascript” 兩書的作者。他為許多公司和組織作過貢獻,例如 Adobe SystemsZumba FitnessThe Wall Street JournalESPNBBC 等 , 也是很多機構的頂級藝術家,包括但不限於 UsherFrank Ocean 以及 Metallica

大多數時間,他都在 San Francisco Bay Area,同這世上最美麗的女子在一起


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章