為什麼程序比較難寫、bug 比較難調呢?
題主對計算機科學常見概念棧、隊列、樹、圖都理解得較好,但是為什麼還是比較難寫好程序、 比較難調好bug呢?
程序處理的難點不在於計算,如果只需要處理計算那倒簡單了;一段代碼表明的實際上是「一段邏輯」,而邏輯是需要判斷邊界條件的。
我是做前端的,就舉個很經典的前端需求:名片卡。也就是你把滑鼠放到某個答題者昵稱上彈出的那個信息浮層。
我盡量用非碼農能聽懂的話解釋一下:在瀏覽器里,是這樣處理滑鼠移入和移出的:
對上面這個藍色區域設定一個監聽器:當滑鼠挪進藍色區域時,會觸發一個事件mouseover,瀏覽器會通知我,告訴我滑鼠挪進去了,然後我去做我想做的事;當滑鼠挪出藍色區域時,會觸發mouseout,也會通知我,我再去做想做的事。
再來看名片卡:一個名片卡當然分昵稱和卡片兩部分:
——————————————————————————————————一開始,B當然是不顯示的,現在用戶把滑鼠挪進了A,於是:A的mouseover被觸發這時要做的事,當然就是顯示出B。那麼好,邏輯就變為:A的mouseover被觸發→彈出名片卡
而當滑鼠移出A時,名片卡就要消失,對吧,那麼再加一個邏輯:
A的mouseout被觸發→隱藏名片卡——————————————————————————————————
看似大功告成了,耶!但是上線後很快發現個問題:
當滑鼠先進入A時,B出現了,這個時候用戶想要把滑鼠挪到B上,去點擊裡邊的按鈕。可是按照上面的邏輯,當觸發A的mouseout時,B是會消失的,於是用戶根本無法點擊B上的按鈕!也就是觸發A的mouseout時既要B消失又要B不消失,真是矛盾啊!怎麼辦呢?於是就想了一個辦法:當觸發A的mouseout時,先不讓B消失,如果判斷隨後滑鼠沒有進入B,再讓B消失;如果進入了B,則不讓B消失。
於是,以下是全部邏輯:
A的mouseover被觸發→彈出名片卡A的mouseout被觸發→先不做任何事→500毫秒後檢測是否觸發了B的mouseover:若觸發了,B不消失;若沒有觸發,B消失。——————————————————————————————————
好啦,再次上線!
但是很快又發現問題了:如果滑鼠先進入A,然後進入了B,然後又從B挪出去了!這樣名片卡就一直存在了,不會自動消失!也就是說,當觸發B的mouseout時,也要讓名片卡消失。於是,以下是全部邏輯:
A的mouseover被觸發→彈出名片卡A的mouseout被觸發→先不做任何事→500毫秒後檢測是否觸發了B的mouseover:若觸發了,B不消失;若沒有觸發,B消失。B的mouseout被觸發:B消失。——————————————————————————————————
好啦,上線之!很快,又有問題被爆出了:
如果滑鼠先進入A,再進入B,但又挪回了A,怎麼辦?按照邏輯,從B挪回A的時候必然要經過一個空白區域,而這是有時間間隔的,那麼就會出現這種現象:挪出B時B消失了,進入A後B又出現了,按照一般的滑鼠挪動速度,這會造成名片卡的一次閃爍,這在產品上肯定無法接受。也就是說,從B挪出去後也要判斷滑鼠的去向!
於是,以下是全部邏輯:A的mouseover被觸發→彈出名片卡A的mouseout被觸發→先不做任何事→500毫秒後檢測是否觸發了B的mouseover:若觸發了,B不消失;若沒有觸發,B消失。
B的mouseout被觸發:先不做任何事→500毫秒後檢測是否觸發了A的mouseover:若觸發了,B不消失;若沒有觸發,B消失。——————————————————————————————————
但幾經周折後,又發現一個問題:
用戶的滑鼠總是會不經意地划過昵稱,他可能不是想要去看名片卡,只是想點擊另一個東西時滑鼠經過了名片卡;但按上面的邏輯,每次划過昵稱後都會彈出名片卡,移出時再消失,於是——用戶會看到不想看到的結果,名片卡亂閃,特別是滑鼠滾輪往下滾的時候...也就是說,需求變成了:A的mouseover出發時,不要立刻顯示B!而是要過一會看看滑鼠還在不在A上!
以下是全部邏輯:A的mouseover被觸發→先不做任何事→500毫秒後檢測是否觸發了A的mouseout:若觸發了,則不顯示B;若沒有觸發,顯示B。A的mouseout被觸發→先不做任何事→500毫秒後檢測是否觸發了B的mouseover:若觸發了,B不消失;若沒有觸發,B消失。B的mouseout被觸發:先不做任何事→500毫秒後檢測是否觸發了A的mouseover:若觸發了,B不消失;若沒有觸發,B消失。——————————————————————————————————
7-21補充:
很快又有問題了!我們來看這張圖:大家來想一下:如果我先把滑鼠移到B,再移到C,會發生什麼呢?按正常人的羅輯,C處於B的內部,所以一直都不會有什麼問題,對吧?錯!坑爹的瀏覽器會告訴你,如果你挪進了C,那麼會觸發B的mouseout!然後觸發C的mouseover!於是B就順理成章地消失了!所以,要增加一個判斷,當觸發一個節點的mouseover時,要看看它是否處於B的內部!這裡有一個技術問題,一個名片卡內的DOM節點少則十幾個多則數十個,難道要給每一個節點都綁定mouseover來判斷?好在瀏覽器有一個機制叫做「事件冒泡」,如果C的mouseover被觸發,它在響應自身的mouseover後,會繼續把mouseover這個事件通知上一層,也就是說B的mouseover也會隨後觸發(是同步的),B當然也會繼續向它的上一層冒泡,一直冒到整個頁面的最頂層document.body
也就是說,B的mouseover被觸發時,事件的源節點不一定是它本身,有可能也是它內部的元素。
所以,只需要在B的mouseover上再加個判斷就好了!
以下是全部邏輯:A的mouseover被觸發→先不做任何事→500毫秒後檢測是否觸發了A的mouseout:若觸發了,則不顯示B;若沒有觸發,顯示B。A的mouseout被觸發→先不做任何事→500毫秒後檢測是否觸發了B的mouseover:若觸發了,B不消失;若沒有觸發,B消失。B的mouseout被觸發:先不做任何事→500毫秒後檢測是否觸發了A的mouseover:若觸發了,B不消失;若沒有觸發,B消失。B的mouseover被觸發:先不做任何事→500毫秒後檢測其事件源節點是否處於B的內部:若屬於,則B不消失;若不屬於則什麼也不做。——————————————————————————————————以上就是一個名片卡如何顯示的邏輯。你可以看看,有多少地方可能出錯?判斷得越多,越可能出現因馬虎大意而造成的bug。所以大家可以去所有有名片卡的網站,如百度貼吧,新浪微博,人人,甚至知乎,可以發現滑鼠放到昵稱後名片卡都不是立即出現的,而是有一定延遲的(已考慮第一次放上去時會發起請求,請求本身會有延遲,但一般第二次之後放上去則會直接讀緩存,不會有請求的延遲),為的就是考慮以上這些情況。不信你看:
這是新浪微博的名片卡:如果你先把滑鼠放到昵稱上,等名片卡出來後慢慢地挪到箭頭所指的縫隙處,會發現名片卡消失了;但如果快速經過縫隙處挪進名片卡,則不會消失,這就是你在500毫秒後滑鼠的去向問題了
這就是一個簡單的名片卡顯示的問題,之所以加粗是因為我還沒細談上面提到的緩存問題:為了減小伺服器壓力,對某一個昵稱第一次顯示名片卡時會發起請求讀取數據,但第二次第三次...就會讀取第一次時存在本地的緩存,而不去讀伺服器。邏輯就是:
觸發某一昵稱的mouseover→查看本地hash表中是否已有緩存:有則讀取;沒有則發起伺服器請求。
而伺服器請求會引起另外一個問題,從發出請求到收到返回是需要時間的,如果在返回之前用戶從該昵稱上挪走了滑鼠,那怎麼辦?一般的做法是拋棄返回數據而不是繼續顯示。如果這裡沒做好,那就可能會出現滑鼠從昵稱上挪開了但名片卡還是顯示出來了的情況。
還有位置計算的問題,名片卡不能一半顯示在屏幕裡邊一半顯示在屏幕外邊,所以要動態計算昵稱距離屏幕邊緣的距離,邏輯就是:
名片卡優先出現在昵稱上方;
如果上方空間不夠則顯示在右側;如果右側不夠則顯示在下方;如果還不夠就顯示在左邊;都不夠那沒辦法了以上不但要考慮名片卡位置問題,還要考慮名片卡上那個箭頭的指向問題,箭頭需要恰好指著昵稱的正中間,而這個是需要計算的
+++++++++++++++++++++++++++++++++
這還不包括名片卡內部的一些按鈕的邏輯,比如下面這個:名片卡里有求關注、取消關注、設置分組、私信!這些邏輯都很複雜!雖然組件都可以服務化但成本也很大!一個組件要考慮各種不同的調用方!你可以看看有多少邊界條件需要考慮,多少邏輯需要處理,隨便哪個地方出個問題,到了測試那裡都是個bug。而這些,以上,僅僅只是一個名片卡的問題!而且我還沒有把後端的bug算進去,比如如果發起請求返回的數據出錯怎麼辦,彈個錯誤提示呢還是不作為呢還是重新發請求?如果後端把名片卡數據打錯了怎麼辦?html模板託管在哪?後端吐出全量html還是止吐數據讓前端去拼模板?這套機制換一個頁面能不能用?(可移植性)如果其他腳本文件沒載入的話還能不能用?(依賴程度)是否可以適配多種樣式的名片卡?(可定製)還有其他的地方,看似簡單的功能,實際的邏輯都非常複雜,並不是外行人想的那樣「這個功能很簡單你很快就能搞定吧」,剛入行的PM都是這麼想的!其實難和不難都是相對的,最重要的是思路。
如果你寫一個程序,越寫越蛋疼,bug 越來越多,那通常是思路在一開始就出現了問題,鑽了牛角尖。這時不妨把有問題的模塊全部刪掉,從零開始換個思路重新寫,退一步海闊天空。
比如說,@貓愛吃魚不吃耗子 在 本題的回答 里這個名片卡的例子:一開始,B當然是不顯示的,現在用戶把滑鼠挪進了A,於是:
A的mouseover被觸發這時要做的事,當然就是顯示出B。那麼好,邏輯就變為:A的mouseover被觸發→彈出名片卡而當滑鼠移出A時,名片卡就要消失,對吧,那麼再加一個邏輯:A的mouseout被觸發→隱藏名片卡
然後就不斷遇到各種各樣的問題,於是最後變成:
A的mouseover被觸發→先不做任何事→500毫秒後檢測是否觸發了A的mouseout:若觸發了,則不顯示B;若沒有觸發,顯示B。
A的mouseout被觸發→先不做任何事→500毫秒後檢測是否觸發了B的mouseover:若觸發了,B不消失;若沒有觸發,B消失。B的mouseout被觸發:先不做任何事→500毫秒後檢測是否觸發了A的mouseover:若觸發了,B不消失;若沒有觸發,B消失。B的mouseover被觸發:先不做任何事→500毫秒後檢測其事件源節點是否處於B的內部:若屬於,則B不消失;若不屬於則什麼也不做。
他的思路本身並沒有什麼太大的錯誤,只不過……真的需要這麼複雜嗎?
讓我們回到開頭,換個思路:不把 B 當成一個完全獨立的東西,而是作為 A 的一部分(子元素)。B 默認是不可見的,當滑鼠停留在 A 上面時才把 B 顯示出來,由於它是 A 的一部分,所以會把 A 的實際面積「撐大」來容納 B。
為了美觀,A 和 B 之間在視覺上是有一條縫隙的,所以在實際操作時需要在 A 和 B 之間夾一個完全看不見的透明層(下圖紅框),它的作用是把 A 和 B 的距離撐開,於是在視覺上就有了一條縫;然而在計算機看來,它們都屬於 A。這樣一來,只要滑鼠不移出紅框,瀏覽器都會判定在 A 的範圍里,所以不會觸發 mouseout 事件,B 也就不會出現意外消失的情況。而且現在再往 B 里放任何東西,無論是按鈕、文字還是圖片都不會出現問題。
現在我一行 JS 代碼都沒寫,上面所說的完全可以用純 CSS 實現:由於 B 是 A 的子元素,所以可以直接用 CSS 的相對父元素的絕對定位來確定顯示位置(可以理解為老爸走到哪孩子就跟到哪),不需要任何複雜計算。至於箭頭位置和屏幕邊緣的問題,也可以先在 CSS 里寫好在上下左右四個方向顯示的子類,JS 判斷 A 在屏幕邊緣時,給 B 加上或替換相應的 class 即可。你看,是不是換個思路之後所有問題都迎刃而解了?
編程是個充滿創造性的事情,每一個需求都有無數種實現方式。所以要多思考,多運用發散性思維來另闢蹊徑。切忌「別人(或自己以前)都是這麼寫的,所以我現在也這麼寫」的想法。再扯遠一點,其實人生也是如此。就當所有人都覺得到了該上學的年齡就得去學校的時候,我卻不一定,誰說就一定不如別人?總之,編程和人生一樣,都是在不斷地否定和創造中實現的。
具體到題主的情況,我倒是建議你多接觸些自然、人文方面的東西,尤其是抽象一些的藝術,或許能有意想不到的收穫。
P.S. 我順手把上面名片卡的例子完善了一下,做成了在線 Demo,用瀏覽器的開發者工具即可看到相關代碼,有興趣的同學請戳這裡:https://www.dandyweng.com/playground/popover-demo/初中生都懂電場和磁場互相轉化,你要不要解解各種介質、各種邊界條件的麥克斯韋方程組試試?
棧、隊列、樹、圖只是看上去好理解而已,你要不要用代碼實現一遍試試?要不要用這些東西解決問題試試?
這個問題不是數學和計算機科學的區別的問題,而是表面上理解一個概念,和能用這個概念解決問題的程度的差異問題。
大部分BUG並非產生於對主演算法,或者主要邏輯的理解偏差,而是在於極限情況的判斷不足,比如除數為零,數學上可以無窮大,但代碼里必須做醜陋的條件判斷。邏輯和邏輯之間的橋接,概念匹配不合理也會導致bug,比如說一個模塊要絕對值,一個模塊傳遞了相對值過去,表面上看邏輯是沒問題的,只能靠調試。典型的代碼就是20%是精巧簡潔精確正交的演算法實現(大家幻想的極客所寫的偉大代碼),剩下80%就是粗陋蠢笨冗長無規律的邊界條件判斷和異常處理和跨模塊的數值轉換匹配(大家幻想的菜鳥所寫的可笑代碼)。大部分bug也都出在這80%裡面,大部分程序員的青春也浪費在這80%的白痴代碼上。如果說做這80%的粗活的程序員是在寫長篇小說的話。那個真正只寫那20%的真理和美的代碼的程序員,只能算是在寫童話而已。(當然不能說童話就一定比小說低級哦)
紙上得來終覺淺,絕知此事要躬行
看到有同學表示我的回答有偏頗,特此作以說明:下面引用了我最開始看到的時候題主的問題原文,沒有刪改,我的回答也是針對這個問題而作的。後來題主修改了問題,我也沒有再回答過。特此說明。題主原問題如下:
計算機科學並不涉及高數、線性代數這類高大上的知識,其常見概念棧、隊列、樹、圖都比較好理解,但是為什麼還是程序比較難寫、bug 比較難調呢?
首先分析題主的問題:
1.計算機科學不涉及高數、線性代數這類高大上的知識;2.計算機科學的常見概念棧、隊列、樹、圖、都比較好理解;3.程序比較難寫,bug比較難調;4.題主認為在上述的1.2兩點成立的前提下第3點不應該成立,但第3點的成立是事實,因而有所疑問。回答如下:
1.關於題主的第1個論點:計算機科學不涉及高數、線性代數這類高大上的知識。首先高數、線性代數並非什麼高大上的知識,作為大學課程的高數和線性代數只是工科專業的入門和基本課程,還有很多遠比這些課程高深的知識。
其次計算機科學中有很多地方涉及到了高數、線性代數以及許許多多其他高深的知識。例如,計算機圖形學中大量應用到了矩陣論等知識,而矩陣論在大學課程中一般是作為線性代數的後續課程學習的。處過矩陣論之外還有很多知識,題主感興趣的話可以自己去查。因此,題主的第一點論點是不成立的。
2.關於題主的第2個論點:計算科學的常見概念比較好理解。
常見的數組、鏈表、堆、棧、隊列、圖、樹等概念的基礎版的確不難理解。但是,由這些概念延伸出的概念並不都是好理解的,如計算機使用的數據結構中,「樹」就有很多種,並不都是非常明白易懂的。由於題主的問題中有常見二字,而我們對「常見」的定義不盡相同,我只能說我對題主的第2個論點持保留態度。
3.關於題主的第3個論點:程序比較難寫,bug比較難調。
一般而言,程序難寫的程度視程序所要完成的功能、要達到的要求以及完成程序的時間等外部條件而定。舉個例子,寫個給二三十人用的聊天室程序就比較簡單,而12306網站的構建則非常困難。所以,作為互聯網從業人員,我只能說,大多數人見到的網站、應用等程序的編寫是比較難的事情。主要的難度在於:1)如何完成用戶所需要的功能。2)如何讓程序穩定可靠的運行,盡量少出問題。3)如何應對大量用戶使用帶來的問題。4)如何隨著用戶需求的變化而在不影響現有用戶使用的前提下對程序加以改進。5)如何在盡量不影響程序運行的情況下改進程序的效率等。(其他的難點就不一一列舉了)
而關於bug的問題,由於bug的存在嘗嘗難以定位和處理,另外,在實際應用中計算機程序的邏輯複雜度非常高,所以,bug是不可避免的。關於bug問題的邏輯分析我並沒有做過,歡迎大家補充。
4.由於計算機科學涉及到了許多複雜的知識,而計算機程序本身具有極高的邏輯複雜度,而我們嘗嘗要在完成指定功能的同時兼顧程序性能等其他方面的考慮,程序是比較難寫的。同時,也是由於程序的邏輯複雜,所以bug難以避免。
5.題主本身的第4個論點中的推理部分:論點1、2成立則論點3 不成立
首先,程序難寫,bug難調的直接原因是程序的複雜性,既包括數據的複雜性,也包括運行過程的複雜性,同樣包括外界限制的複雜性,因而論點3 的成立並不依賴於論點1和論點2。舉個例子,英語就是26個字母(基本概念簡單易懂),在所有語言中算是比較易學易用的,學習難度完全無法和漢語相比,不涉及高大上的文字體系(不涉及高大上的知識),但是用英語寫出好文章依然很難(程序難寫,bug難調)。
其次,題主的論點1.2並不正確,原因我在上面已經分析過了。綜上,題主問題中的推理並不正確,前提條件也不正確。所以才會出現自己推理出的結果與實際有所偏差的情況。
PS:水平有限,歡迎大家糾正或補充。程序的核心是邏輯。寫程序就是完成一個完備的邏輯流程,包括各種情況的處理。Bug就是有些情況沒有考慮到導致的。還有些Bug是調用下層庫方法有誤、下層庫錯誤,甚至是編譯器Bug導致。說白了,就是邏輯複雜度太高導致。至於數據結構的複雜度並不高,大部分Bug不是由它們導致。
八成在於不知道現在的需求,二成在於不知將來的需求
棧、隊列、樹、圖這些東西都已經是前輩們對一些現實問題經過總結後抽象出來的計算機世界的模型。對於這些抽象出的模型討論編程是否困難,查BUG是否困難沒有任何意義。因為在你學棧、隊列、樹、圖這些模型時,其實寫程序最痛苦的事情你都還沒有開始。
私以為寫程序的本質是對現實世界中的某個問題,在計算機的規則世界中抽象出一個解決方法。
仔細想一下,就算平常人和人之間,對於一個人拋出一個問題,另外一個人都有可能理解岔了給出一個錯誤的答案。而且這還是在人類思維方式基本相近的前提下。考慮在用計算機給出解決方法的目標背景下,考慮以下場景:
1. 人類甲給出了一個問題A,需要一個答案B(姑且理解為「需求」)來解決問題A。2. 人類乙按自己熟悉的思維方式理解了問題A(假設人類乙理解後的問題是A』)3. 人類乙根據自己理解的問題A『在腦內抽象了一個在計算機規則世界中呈現的問題A4. 人類乙根據上述問題A設計了一個解決方法(假設人類乙設計的解決方法為B)5. 人類乙根據設計出的解決方法B』『,用編程語言實現了解決方法B。以上過程還沒有考慮到軟體工程中眾多干係者參與在內從而產生的交流過程中的信息失真等情況。
在如此一個「腦力密集型」的勞動中,只要在上述整個過程中,思維上存在一點點理解的偏差,一點點的理解不充分,哪怕是一點點的疏忽, BUG就被做進去了。而且大部分情況下,BUG並不會立刻體現出來。因為一個程序是多人腦力的結晶,它是如此精密而複雜(不管怎樣,就算是一個沒有經過充分設計的「爛程序」, 它也仍然擁有很高的複雜度),以至於BUG要麼在平日里掩藏起來,只有滿足了某些特定條件,它就以程序員最匪夷所思的形式發作。所以當調試代碼時,你要考慮的是 如何從這個匪夷所思的現象,回溯上述的場景1~6(而且,負責找問題的人很可能根本就沒有參與這個1~6的過程),找到問題所在地,這是一件多麼痛苦的事情。當然你手頭會有很多工具幫你去搜集程序出錯時的蛛絲馬跡,從而提高找到問題真正所在地的效率,但這個過程仍然無比痛苦。
最後再舉一個例子:Perter Van Der Linden在他的《C專家編程》(Expert C Programming)中舉了一個價值2000萬美元的BUG的例子。從書中描述來看這只是一個誤將 賦值符號"=" 錯寫成了比較符「==」的錯誤,看上去這是一個似乎很簡單的問題,但有理由相信作者他們在面對操作系統所表現出的不正常而經過一輪輪調試調查後鎖定到最可疑問題點時,他們面對著個"=="符號,也就只能苦笑一下了。
向所有將熱血奉獻在調查BUG的技術者致敬~課堂里:程序 = 數據結構 + 演算法 這是為了打基礎實際上:你現在和將來寫的程序,調的bug 幾乎沒有和棧、隊列、樹、圖有直接關係的。你面對的是無限的細節與動態複雜度以及無窮的未知。這就像會開槍和會打仗是完全不同的兩件事。
讀一篇別人的文章,找出錯別字和語法錯誤出來。這就是找bug。重新根據要求寫一篇出來,就是重寫代碼。
概念是好理解,但是把你理解的概念實踐出來,是非常困難的。從理論到實踐有太多的問題要解決。理論是在理想狀態下的一個優化的抽象,而實踐是個跨域合作的混亂環境。要把現實環境理順再在有限的接近理想狀態下做出了一個相對合理的理論實現,是要一個很長的過程。
好比去火星的理論已經非常完備了,但是還是至今沒有人干嘗試去火星。就是去月球,已經有人去過了,理論已經很完備了,但是至今沒有任何國家冒險去月球旅行一次了。
回到計算機上,理論到實踐有很多困難。主要克服一下幾點:
1. 硬體限制
硬體限制之一空間,大部分理論上的數據結構,都是理論上可以保存無限數據的結構,然而現在電腦的內存+硬碟,可能並不允許產生無限大的數據。速度上來說,歷遍任何一個數據結構,都是要相當長的時間,數據越大,時間越長,但速度不是主要的,只要數據結構做成了,歷遍一次只是時間問題。然而數據結構並不是只有歷遍,還有刪除,添加,甚至排序等操作,但是由於cpu,內存,硬碟的速度限制,可能會很慢,在應用的過程中可能並不理想。
然而硬體最大的問題並不是空間和速度,而是硬體是有生命周期的,會故障,會老化,甚至是突然毀壞,在你實踐過程中,尤其是做產品,這個必須考慮的問題。
2.語言本身的bug
語言是有bug的,有的是天生的的,有的是硬體造成的,有的是歷史原因,有的就是故意設計成那個樣子的。 (javascript有個項目叫做wtf.js,專門記錄js的怪異之處)。編寫一個代碼有時候不知道它的bug在什麼地方,然後就出錯了;有的時候升了級,然後本來沒有錯誤的,現在有了;有的時候知道了,繞過錯誤避免了錯誤,但是問題很多。實踐中,語言本身的bug是要面對的3.錯誤處理
程序編寫完了,然後有很多實踐中才會有的錯誤,你如何去處理這些錯誤,也是非常重要的,這樣才能保持程序的穩定。有硬體錯誤,比如內存硬碟容量不夠了怎麼辦?
有軟體錯誤,比如指針指錯了位置,然後如何處理把指針回歸的真確的位置上?超出了定義邊界,如何處理等等錯誤都需要你去思考4.文字編碼
理論上的數據,一般都是用英語字元的。但是現實中太多的文字編碼,現在在用uft8等標準在統一,但是事實上,不是所有人都用uft8,尤其是歷史項目,都不是uft8,這樣,你以為你存的是對的,但最後取出來的是亂碼。5.數據結構內部保存內容
你打算讓你的數據結構保存什麼類型的內容,這個也是一個問題。一個鏈表的一個數據,可以是字元,也可以是另外一個鏈表。一些語言只需管你的邏輯結構,其實很好處理,但是如果語言不但要保證邏輯結構的正確,還要保證物理結構正確,那麼就十分困難了。6.數據保存的結構
你用數據結構保存了那麼多內容,你如何保存,下次如何載入,又是個問題了。當然可以放在資料庫內,也可以放在自己定義文件中,但如何快速存放,又如何快速載入成員了的數據結構,這又是問題了。這裡還有個問題,萬一這個文件被篡改,萬一結構被破壞了,你又如何處理,又如何儘可能的恢復這個數據結構的內部結構和數據的完整性。
7.安全性
如果你要保存的內容非常敏感,有如何保證其安全性,如何在數據結構內的快速加密和解密?在這個過程中,又能保證其不被輕易的破壞8.其他問題
實踐過程中有太多可以總結的問題了,有些是經常會遇到的,有些是你沒有遇到前,更本沒有辦法想到的問題。如何讓開發者方便的使用,提高簡潔的API。理論很完善了,是不是要一套行業標準,來統一使用方式,給出統一API。
如何快速檢測你演算法作出來的數據結構是正確的,如果發現有節點錯誤,如何快速糾正把節點放到真確的位置上。如果TB級的數據上,進行快速的數據操作和處理,等等等等,等待你的思考和解決資本論不過幾本書而已,也不難懂,但是我國不還是處於且將長期處於社會主義初級階段么
先來糾正幾個錯誤的觀念,再來解釋原因。
計算機科學並不涉及高數、線性代數這類高大上的知識
這句話不對,計算機科學很多都用到了高數和線性代數,來舉幾個例子,高數當中的牛頓迭代可以作為逼近數值的方法,傅里葉變換又是信號處理的基礎,圖形計算里大量充斥著矩陣計算。
其常見概念棧、隊列、樹、圖都比較好理解
棧,隊列,樹是實現的方法而不是解決問題的方法,圖更是一個比較廣的話題,從實現方法上來說圖有多種實現方法,從演算法上來說,根據圖本身的性質演變出來的演算法又有很多,數學上有專門的圖論分支。
程序難寫,bug難調,因為- 解決問題的方法多種多樣,舉一個簡單的例子,從簡單的文件搜索來說,正常人的想法是根據輸入搜索的信息,一個文件一個文件的尋找,看是否符合關鍵詞。那是不是有更加快捷的方法能夠找到文件呢?這個方法肯定更加複雜難寫,更加難以實現。
- 解決問題的方法有問題,再來舉一個簡單的例子,設計一個銀行系統,可以轉賬。
本人賬戶=本人賬戶-轉賬數目
目標賬戶=目標賬戶+轉賬數目
這樣看好像沒有問題,但是如果考慮到轉賬數目可以是負數的話。。。。問題就大了。即使限定了數字必須大於0。這個演算法在實際操作當中還是有問題,比如如果執行完了第一句話,機器崩潰了,那是不是這部分錢就不翼而飛了?所以這種情況也會造成bug。
- 實現的方法有問題,再來舉個例子,你的解決方法需要用圖來解決這個問題,結果這個圖的結構太複雜,你寫的程序根本不是按照你的思路來運行的,這也會造成bug,這就是演算法對的情況下,你的代碼有問題。
我是來吐槽「其常見概念棧、隊列、樹、圖都比較好理解」。。。當你遇到有數百萬的節點的圖,數百萬節點的樹,你還拿最簡的方法去處理,肯定哭瞎了好么?就一個樹,還有B+ B-樹,紅黑樹等,每一個都不簡單。
這是一個依賴邏輯建立起來的世界,在這裡你無法依賴上帝建立起來的自然法則。你本身就是你建立的邏輯世界的上帝,需要操心一切細節,制定一切規則。沒有規則或規則不完善的地方,那就是你創造的世界的缺陷,就是bug。而建立一個自洽的世界並不是那麼容易的。因為你畢竟是人,而不是神。