程式鬼打牆,海森堡測不準原理
最近這兩天都遇到鬼打牆事件,明明預估很簡單輕鬆,可以提早下班的工作,卻連兩天都遇到當場傻眼無法解釋的現象,弄到加班都差點搞不定。
其實軟體開發就像是堆疊黑盒子的積木,你很難確定一個函式呼叫後,底層到底是怎樣運作才生出你所渴望的回傳值?
今天我遇到一個鬼打牆的狀態,那現象就像是你要學生背英文單字,要求他邊寫邊大聲地唸出來。你看他唸得字正腔圓,紙上書寫的單字也拼得正確極了,於是乎你放心的說:這些單字你都會了,默寫就好,不用唸出聲。但結果你回頭檢查紙上默寫的單字,卻每個全都拼錯!?
大驚之下,你又要求學生邊寫邊唸,結果又是全都正確,但是等下默寫、又全拼錯了...。
如果你是老師,八成這時候已經認定這個學生性格乖戾在惡搞你,恨得心癢癢拿起教鞭痛扁這劣徒一頓。但是,我這次所遇到的這個學生,卻是一支 .NET Framework 標準 Class Library 內的標準 API。
上面寫的,是給不懂程式開發的朋友理解我所遇到的鬼打牆狀況。這種現象,像極了我物理界背景的朋友耳熟能詳的量子物理現象,測不準原理。
先聲明,這是一個單一處理程序與單一執行緒簡單到不行的程式。
整件事就是開發時期,為了仔細觀察資料的變化,我會不斷把資料每個步驟後的狀態都輸出到螢幕上 ( Debug.WriteLine( ... ) ),確定他的變化如我預期。等到確定資料的每個狀態都正確後,我唯一做的事情就是把輸出到螢幕上的程式全部移除,交給客戶測試。沒想到資料結果怎麼測怎麼錯,連我自己機器上也跑不出正常預期的結果,我在客戶面前簡直是傻眼變成張口結舌的阿呆,五分鐘前那句「我都測過,絕對沒問題。」無疑是當場賞兩巴掌讓我信用破產。
我不敢吭聲地趕緊把剛剛移掉的那些、用來把資料輸出到螢幕上的程式復原回來 ( 就只是單純的把註解掉的 Debug.WriteLine( ... ) 回復,絕沒有更多多餘的動作 ),準備開始除錯找出問題,結果一切又都正常了。
「可能我剛剛版本更新沒有成功吧?」我心想,然後又把那些輸出螢幕的程式移掉再更新一次,靠!程式一跑資料又全錯?
這件事真的是反反覆覆搞了我快一小時那麼久,客戶在旁看我這跳樑的小丑都快打嗑睡了。當然,這件事我最後找出可以理解的原因,也避開了這個問題,不過理解原因後,不免要對微軟的 .NET Framework 的 API 不夠直覺的設計缺失感到不悅。
如果大家對這個程式的測不準原理的現象感到好奇想體驗,我提供以下的程式碼讓有興趣的人玩玩,至於造成此現象的原因,就留給大家自行參透吧 (或我下次在撰文解答 ):
程式說明:這是一個處理以下 XML 文檔的簡單程式,
<Root>
<A/>
<B>
<BB/>
<BB/>
</B>
</Root>
程式要取出所有 <BB></BB> 內的資料,並且從整份 XML 內移除 <B></B> 節點 ( XmlNode )。
Code (.Net 1.1 & C#):
string sXml = "<Root><A/><B><BB/><BB/></B></Root>";
XmlDocument oDoc = new XmlDocument();
oDoc.LoadXml(sXml);
// 關鍵來了
XmlNodeList oBBNodes = oDoc.SelectNodes("//Root/B/BB");
// Debug.WriteLine("oBBNodes.Count=" + oBBNodes.Count);
XmlNode oBNode = oDoc.SelectSingleNode("//Root/B");
if (null != oBNode) oDoc.DocumentElement.Remove(oBNode); // 從原本的 XML 文件中拔除 <B></B> 以下的整個節點
Debug.WriteLine("oBBNodes.Count=" + oBBNodes.Count);
這樣你就可以看到這懸疑的現象了。oBBNodes 明明就應該有兩個節點 ( Count=2 ),但是執行以上的程式,螢幕上卻會得到 "oBBNodes.Count=0" 的答案。
但是如果把第六行的 // Debug.Wr..... 的註解恢復成可執行的 Code,結果螢幕上跑出來的輸出卻是:
oBBNodes.Count=2
oBBNodes.Count=2
螢幕重複兩次輸出,但都是正確答案。
哈,真的是很像測不準吧。如果不想跑出第六行的螢幕輸出,卻又想呈現最後正確的結果,可以把原本第六行的程式改成 "int n = oBBNodes.Count;" 做個愚蠢的虛功即可。
真的是一整個蠢到不行。
留言
張貼留言