從閱讀代碼上考慮,if里的">"、"<"用哪個更容易看懂?

輪子哥說的不錯,大家沒人看過《代碼大全》嘛,裡面說的有,輪子哥的這種用法是最推薦的


這種類型的規範只要統一使用一種就行,沒有大的影響。別這塊代碼用這個風格,另一塊代碼用另一個風格,那就很糟糕。一致性很重要!


如果單論可讀性與合理性,其實沒有if更容易看懂。我們舉個極端點的例子:

按照學生分數給出評價,&>90=A、&>80=B、&>70=C、&>60=D、~=E

這個題目,最直觀的數學表達應是這樣的:

0 -E-&> 60 -D-&> 70 -C-&> 80 -B-&> 90 -A-&> 100

諾,這麼一條坐標軸是不是非常直觀明了?但為什麼到了代碼里,我們卻都寫成了這樣:

level = {
if (score &> 90 {
:A
} else if (score &> 80) (
:B
} else if (score &> 70) {
:C
} else if (score &> 60) {
:D
} else {
:E
}
}

這還是連續分段情況,如果不連續,比如:

水槽情況預警,要求:低於水位線20或高於水位線30時給出警告,低於30高於40給出錯誤

數學坐標軸:

-00 -ERROR-&> -30 -:WARNING-&> -20 -&> 0 -&> +30 -:WARNING-&> +40 -ERROR-&> +00

到了代碼里就更丑了:

status = {
if (hight &< -30 or hight &> 40) {
:ERROR
} else if (hight &< -20 or hight &> 30) {
::WARNING
} else {
:nil
}
}

這尼瑪啥玩意兒?這叫可讀性?無論你是 (h &< -a || h &> +b) 還是 (h &< -a || +b &< h) 還是 (h &> +b || -a &> h) 都一樣噁心(當然這些中我更推崇第二種,畢竟好歹也體現了一點直觀坐標軸)。

那麼我們該怎麼Coding更好呢?為了簡單起見,先看第一例:

score =&>
arrive(point) = point &> score
index = [60, 70, 80, 90, 100].index(arrive)
level = [:E, :D, :C, :B, :A][index]
=&> level

ok,簡單明了,我讀給你看:1. 輸入score,2. arrive的真值等價於某個point大於給定score,3. index的值等於坐標軸上找到arrive(為真)的位置,4. level的值為index對應位置上的原子, 4. 輸出level。整個代碼塊的職責:輸入score,找到score的level,輸出level。

可以看到除去添加輸入輸出與一些更嚴明的界定,思想與數學基本保持了一致性。

更進一步,我們去掉硬編碼:

score =&>
arrive(point) = point &> score
index = range(60, 100, 10).index(arrive)
level = range(E, A, -1)[index]
=&> level

再進一步,我們保持編碼內信息正交 (主要是range(60, 100, 10)與range(E, A, -1)這兩組,其實交叉了「分五個等級」這個硬編碼的信息)

(score, limit=5) =&>
arrive(point) = score &> point
index = 100..0 |&> step(-10) |&> take(limit) |&> index(arrive) |&> else(limit-1)
level = A..Z |&> step(1) |&> take(limit) |&> fetch(index)
=&> level

好吧可讀性又變差了,但功能其實加強了,現在整個代碼塊可以簡單的讀作:輸入score與可選的、默認為5的limit,找到limit段數內score的level段,輸出level。

(這個例子我著重修改了一下,來表現函數式的魅力,比如index行現在可讀多了:100到0範圍內,步進為-10,取前limit個,找到arrive的位置,找不到則為limit-1)

更進一步,我們可以對第一個range的三參(-10)和第二個range的三參(1)進行提取,因為這兩個參數實際隱含了「10分數跨度對應1等級」這個信息。具體例子我就不寫了。

(因為例子修改了,現在不是三參了。之前的range(start, end, step)的格式三參實際是現在的step。所以我這裡其實可以直接說:「我們可以對step進行提取」這樣。但我保留之前的說法,就是為了展示什麼就叫可讀性。)

我們來考慮另外一個問題,這段編碼中有一個中間變數 "index" ,這個變數是有必要的嗎?不是。因為我們的數學坐標軸上沒有它。仔細看數學表達,「0 -E-&> 60 -D-&> 70 -C-&> 80 -B-&> 90 -A-&> 100」,發現了嗎?字母與數字混在了一起。到了代碼里,我們其實把他們分開了,數字組編碼為index行,字母組編碼為level行。這是對一些編程語言,特別是早期面向機器的一些語言的「同構數組|列表」做出的妥協。但其實現在很多高級編程語言越來越面向人類了,異構數組|列表是支持的(多大點事兒嘛)。那麼我們的Code又有新范兒了:

score =&>
arrive([level, point]) = point &> score level
level = [(:E, 60), (:D, 70), (:C, 80), (:B, 90), (:A, 100)].find(arrive)
=&> level

(哦不對,這還是同構的,是tuple&的同構list,但是tuple是異構的。真異構應該這麼寫:"[:E, 60, :D, 70, :C, 80, :B, 90, :A, 100] |&> unzip(2)",看起來更接近坐標軸了,但有點過度刻意追求了)

同樣的,level行的那個硬編碼的映射表可以優化:

level = [(A+i, 100-10*i) | i in 0..5].find(arrive)

不熟悉這種編程風格的人第一眼就會覺得好恐怖,於是潛意識就不讀了,但其實只要你理解其思想,數學又學的不錯(不錯就行,只要你有高中水平的數學知識,這行里的什麼列表推導、二元組之類的真*高級語言概念都應該天然理解),就會發現這樣的編碼極為可讀,數學的魅力就在於符號化的表達,邏輯嚴謹又簡潔有力,這其實才是我心目中的可讀性,畢竟程序是重邏輯的。但如果你的代碼重業務,還是不要做到這一步,畢竟什麼東西過度了都不太好(尤其你參與的是一群碼農合作的項目時,這麼寫小心別人打屎你。不過一般來說這種項目使用的那些假*高級語言里,你想這麼寫也沒辦法)。

甚至,連這行的這個列表推導都可以繼續優化,比如用一些真*高級語言提供了的一些高階函數:

range(0, 5).map(${A+$, 100-10*$})

但這步優化是有帶商榷的,一方面,這麼寫後,對傳統程序員稍微友好了一點點,畢竟Java也在8版本引入了這些東西:

IntStream.range(0, 5).map(i -&> new AbstractMap.SimpleImmutableEntry(A + i, 100-10*i))

但另一方面,對數學又不那麼友好了,又不能直接的讀出,所以可讀性毫無疑問下降了很多。

最重要的是性能問題,這麼寫,實際在運行時增加了創建映射的開銷,畢竟不是硬編碼好的。雖然說對於真*高級語言,不是問題,因為可以在編譯階段完成宏展開,但對於Java這樣中不溜的就難辦了。可能你不得不自己搞些單例啊、靜態變數啊之類的來優化每次執行的創建開銷。

哦多,扯遠了,下面我們再看看例二的需求,怎麼Coding更好呢?

同樣,先建立數值與坐標軸的映射:

[(:ERROR, -30), (:WARNING, -20), (:nil, +30), (:WARNING, +40), (:ERROR, +INF)]

後面的一切還用我再說嘛。。。

最後,細心的童鞋可能會發現,以上代碼,除了那行Java的意外,居然 全 部 都 是 偽 代 碼!雖然不是教科書那種原始的機器偽代碼,但確實沒有一種語言能夠編譯通過。。。這意味著,好吧,只是「理想編碼」。實際編程實踐中,視所用語言,可能都不得不在某些地方做一些修改妥協。。。

最後的最後,給大家一點小建議,所謂見人說人話見鬼說鬼話,寫代碼也是這樣。什麼時候該寫什麼風格抽象到什麼程度,第一看其它參與者是碼農還是碼痴,第二看項目重業務還是邏輯,第三看語言偏工程還是玩具。


if ((test &> 1) (test &< 5))

{

alert();

}

無論邏輯簡單還是複雜,都大括弧換行,括弧加好,寧濫勿缺


我覺得挺好的

單行加不加括弧看在寫什麼,我習慣cpp/java/js就加,kotlin/c#這種不加


我只覺得C系列語言里,通常都要求把等值比較寫成

if(1==i){...}

這個比較反人類,很不喜歡,可是沒辦法,都要求這麼些寫,不這麼寫都不行。


if(test&>1){

}else if(test&<5){

}else{

}

1.單從理解的角度來說,我覺著這樣更好理解。

2.個人習慣,if語句只有一行也要用{}。


這樣寫是很方便閱讀呀。


難道不是看代碼給誰讀嗎


個人常用「自述式編程」(self-documenting code)的方法。

// JavaScript

const testIsBetweenOneAndFive = test &> 1 test &< 5; if (testIsBetweenOneAndFive) { // code }

這讓人可以以自然語言來明白你的「意圖」,如果需要,再去檢查你具體落實意圖的「做法」。閱讀代碼的時候 80% 的情況我們希望搞懂「這段代碼想表達什麼意思」,少數情況才需要追問「這段代碼怎麼表達它想要表達的意思」,用這種方式可以很好地解決前面那個問題。

當然題目中的例子並不是一個真正具備業務意義的意圖。更好的意圖可能是 userIsInfant 或者 ageIsInRange 之類的描述。


語言律師會告訴你 if ( isSuccess() ) 要寫成 if ( true == isSuccess() ) 這樣對讀代碼的人來說清晰明了 true寫前面還能防止漏寫等號 是不是很棒棒 我的觀點是 這都看不懂 明天就不用來了


謝邀

如果出現的情況少

而且邏輯沒有那麼複雜的話

實際上怎樣都沒差

糾結這點的時間都夠別人讀懂了

如果邏輯比較複雜的

應該把上下限和測量值計算好存入兩個變數

這樣在條件中只有三個變數之間的關係

也是比較好懂的

如果經常出現判斷測量值是否在某個域中

應該對區域的概念進行封裝

然後對這個封裝增加介面

// 對於第一個例子

check(test).betweenNotEqual(1,5)

// 對於第二個例子

check(test).outOfNotEqual(-1,5)

// 如果包含邊界

check(test).betweenOrEqual(1,5)

check(test).betweenOrEqualLeft(1,5)

check(test).betweenOrEqualRight(1,5)

check(test).outOfOrEqual(-1,5)

check(test).outOfOrEqualLeft(-1,5)

check(test).outOfOrEqualRight(-1,5)

╮( ̄▽ ̄)╭ 反正工程不是競賽

多封裝這一點點也不會扣分

競賽不用考慮可持續發展

24小時造一座橋過一輛車就行

工程需要考慮持久提供服務

24個月造一座橋持續運維24年

直到新的交通方式出現後結束生命周期


如果你總是為這些問題發愁,我建議你用python。


推薦閱讀:

攀岩和編程有什麼共通點嗎?
大力出奇蹟——Python暴力猜解Web應用
C++學習第一課,之「Hello world」
比特幣入門教程

TAG:編程 | 代碼閱讀 |

分頁阅读: 1 2