閒聊 - 反思我程式寫法的演化


今天有個同學請教我, 我的作業裡面這一行Code的意義是什麼?

bool[] boolArray = new bool[bitArrayCollection.Sum(b => b.Length)];

如果是四年前的我, 可能也看不懂這一行.
而我, 其實也不是為了說要寫得很炫, 讓人家看不懂才這樣寫.
用意只是為了少打幾行Code, 而且讓文字字面單純而了解意思, 才這樣寫
可以看到裡面有幾個關鍵字 "Collection"、"Sum"、"Length"
它作用是bitArrayCollection這個集合裡所有物件的Length屬性... 加總起來(Sum)
這是LINQ用法

ok
從出社會工作一直到現在, 再加上大學四年的學習, 
寫程式的時間已經超過10年了,
而這10年裡面, 其實我一直有在做一種思考,
就是"我這幾行Code怎麼寫比較好?"
也包括我在看別人的Code時感覺到什麼?

幾天前在PTT資訊工作板
有個人回文說他20年前在電子街遇到一個國中生, 超厲害
他寫的Code連旁邊的碩士都看不懂, 於是他就下定決心不加入資訊行業的戰局
接著
下面數個留言說... 是那個國中生不懂程式

哈哈, 對這應答真的感觸良多

現在用一個實例來描述這十年來在我身上發生的演變...
這邊有三個東西 b1, b2, b3. 他們的長度分別是10, 20, 30
BitArray b1 = new BitArray(10);
BitArray b2 = new BitArray(20);
BitArray b3 = new BitArray(30); 
然後為了解說方便, 先用某個容器裝有它們
List<BitArray> bitArrayCollection = new List<BitArray>();
bitArrayCollection.Add(b1);
bitArrayCollection.Add(b2);
bitArrayCollection.Add(b3);

現在我們要加總他們的長度, 就好像算學期總分一樣

一開始學程式的時候, 我們用超笨的寫法

int sum_v0 = b1.Length + b2.Length + b3.Length;

沒錯! 有什麼加什麼!
接著, 老師說總不能一百個東西也這樣加吧, 於是我們學會了陣列:

int sum_v1 = 0;
int i;
for (i = 0; i < bitArrayCollection.Count; i = i + 1)
{
  sum_v1 = sum_v1 + bitArrayCollection[i].Length;
}

那時候會搭配迴圈, 自由地操作陣列的人, 就已經很厲害了!
要看懂他的程式也不是隨隨便便就可以看懂,
尤其是到了二維, 三維, 一層兩層三層迴圈
然後如果再加上一些小技巧, 比如指標
比如 i = i + 1 變成 i++
sum_v1 = sum_v1 + bitArrayCollection[i].Length 變成 sum_v1 += bitArrayCollection[i].Length
就會讓程式變得更讓人看不懂!
學習階段的時候有一種感覺就是, 程式寫得越讓人看不懂就越厲害!

接著, 我就出社會去工作了, 有幸派遣到台積電! 維護機台自動化程式(VB)
這工作不是從沒有寫到有
而是前人寫好的, 我繼續改, 持續加, 以達到新的需求
你知道嗎? 這是一個非常好的學習機會
這上千上萬上億行的程式在我眼前, 我可以好好學習有哪些我不知道的技巧! 別人的技巧.
當我看阿看, 寫阿寫,
我領悟到一件事, 程式要寫得容易閱讀並且效能好, 有彈性才是好程式.
寫得讓人看不懂並不厲害! 而是害人... 害後面的人...

所以我在某些地方會用笨的寫法, 用意是讓人家清楚知道有哪些東西.
而有些地方為了彈性、效能, 用複雜而豐富的寫法. 並且加很多註解.
這是前人留下來的Code教我的事.

接著
因為接觸物件導向的新工作. 我被迫得新學C#...
因為C#, 我學會了 foreach 這樣東西

int sum_v2 = 0;
foreach (var b in bitArrayCollection)
{
  sum_v2 += b.Length;
}

喔~ 這真是個好東西, 不需要再去理會index從哪裡到哪裡啦~~ Ya~~
而且字面上很容易了解阿! "each" 每一個!
恩~ 我本來以為, 程式大概演化到這裡就不會再變了吧.
後來又因為新工作要學WPF, 順便也認識了LINQ這樣東西.
真正神奇的東西來了...
一種像是操作SQL語法的程式語法... 只要你會資料庫應該都知道他在幹嘛!

var query = from b in bitArrayCollection
                 select b.Length;
int sum_v3 = query.Sum();

針對那個集合詢問Length欄位值, 得到結果後加總.
操作物件的屬性像操作資料表的欄位一樣!
學會了這樣東西很高興, 有超多本來要寫迴圈自己去挑選的東西
變得用LINQ就可以挑到了, 就像SQL.

但我是如此的好奇, 也有一點貪心
好奇他到底為什麼可以讓我們這樣寫, 還有... 可不可以再少寫一些東西 XD
每次都要打 from xxx in xxxx select xxxx.xxxx 好累!
於是去看了幾本LINQ的書...發現, 這些其實都只是物件提供的方法!
而像SQL語法的寫法, 是編譯器額外允許你這樣去兜的!
所以, 更直接簡短的方法就出現了:

int sum_v4 = bitArrayCollection.Sum(b => b.Length);

我覺得東西越少越明確.
尤其是加上變數名稱都命名得有意義 ,排列方式就像演算法一樣
這樣看程式就好像看文章一樣, 真是美好~

不過LINQ並不是萬能的!
他會整個集合巡禮... 所以如果為了效能, 不可以隨便用這東西
所有東西or寫法都是用在恰當的地方才會顯得厲害.

如果你有一群東西, 想透過Key就拿到特定物品. 推薦 Dictionary
Dictionary 是一個效能很好的東西, 是我在上一份專案工作時學會的.
感謝那些同事.  :)

留言

這個網誌中的熱門文章

HTML - CSS - footer floating toolbar bottom 瓢浮置底的工具列

jQuery - header & floating menu - dynamic detect & adjust

自己設計讓網頁支援多國語系的架構