左閉右開,這是最基本的代碼準則。

區間或範圍在編程世界中無處不在。典型的例子是選擇開始和結束日期,就像你在 Airbnb 上預訂房間或預訂航班時那樣。這樣的例子有很多:從切片 JS 數組,到 Java 的 List#sublist 甚至 SQL 的 LIMIT 運算符,區間無處不在。

你有沒有想過爲什麼它們的設置總是 [closed, open) 而不是[closed, closed]?

1 什麼是 [closed, open) 區間?

一個閉 - 開區間,通常表示爲 [a, b),這是一個縮寫形式,用於表達 a <= x < b 的所有值的集合,從 a 開始幷包括 a,到 b 但不包括 b,例如,如果我們使用整數,那麼 [0, 5) == 0, 1, 2, 3, 4。

一個閉 - 閉區間,通常表示爲 [a, b],是包含最後一個值的區間,例如 [0, 5] == 0, 1, 2, 3, 4, 5。

2 永遠不要使用 [closed, closed] 區間

幾年前,我有幸在一個廣泛使用 [closed, closed] 區間的系統中工作。該系統在大多數情況下能良好運行,但也需要有大量的笨重的代碼來處理一些少數突發情況。以下是我們必須應對的一些情況:

空的區間

假設你想描述一個從時間 T=1 開始的零長度時間區間(即空區間)。這對於閉 - 開區間來說很簡單,簡單地說就是 [T, T),但對於閉 - 閉區間…… 就不是那麼回事了。你可以嘗試 [T, T-1],但這有點問題,如果 T 是十進制數那麼將無法實現。

按時間拆分

想象一下,你想對註冊到你站點的用戶按時間對他們進行分組,例如每小時分一組。這本質上是將一天中的 24 小時以 1 小時爲區間 “拆分” 的問題。

通過 [closed, open),你將得到一個很好的序列:[0, 1)、[1, 2)、[2, 3)、[3, 4)……[23, 24)。

通過 [closed, closed],你將得到一個奇怪的序列:[0, 0:59:59]、[1,  1:59:59]……[23, 23:59:59]。請注意,這實質上會在你的整個系統中強制執行一個精確度。在 0:59:59 和 1 之間有用戶註冊麼?這些用戶會被落下。

計算區間的長度

每當涉及區間時,另一個常見的任務是計算它們的長度。

對於 [a, b) 很簡單,它只是 b-a。

但對於 [a, b],會遇到一些邊緣情況,例如如前所述[a, a-1] 的長度可能爲 0,或者如果 a < 1,則它可能爲負。

你期望從正確實現的區間中獲得的另一個屬性是,將區間分成兩半應該會產生兩個小區間,這兩個小區間長度加起來等於原始區間的長度。

例如,將一天中的小時數分成兩個區間,應該得到長度爲 12 的 2 個區間,即 12+12 = 24。但使用 [a, b] 區間時會丟失此屬性。

3 最後的想法

在我寫這篇文章時,我發現了 ol’ Edsger W. Dijkstra 在 1982 年寫的一篇簡短的筆記,講述了他爲什麼更喜歡 [closed, open) 區間。我不會用細節來煩你,但我只想說施樂帕克研究中心(Xerox PARC)的那些聰明人嘗試了它們,發現 [a, b] 範圍會導致錯誤和更復雜的代碼。

我希望這篇簡短的文章能讓你認識到使用 [closed, closed] 區間的分險和陷阱。我猜測,人們有時喜歡使用 [closed, closed] 區間的原因,是它們看起來漂亮且對稱,而且大部分時間能正常工作。

只有在極端情況下,它們纔會開始崩潰。但這正是你應該評估設計好壞的關鍵:對其邊緣情況進行測試。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/PttGj78Wz_2VvLoxu1cvcw