2011年10月29日 星期六

python list排序

以下是sort的具體實例。
實例1:
>>>L = [2,3,1,4]
>>>L.sort()
>>>L
>>>[1,2,3,4]
實例2:
>>>L = [2,3,1,4]
>>>L.sort(reverse=True)
>>>L
>>>[4,3,2,1]
實例3:
>>>L = [('b',2),('a',1),('c',3),('d',4)]
>>>L.sort(cmp=lambda x,y:cmp(x[1],y[1]))
>>>L
>>>[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
實例4:
>>>L = [('b',2),('a',1),('c',3),('d',4)]
>>>L.sort(key=lambda x:x[1])
>>>L
>>>[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
實例5:
>>>L = [('b',2),('a',1),('c',3),('d',4)]
>>>import operator
>>>L.sort(key=operator.itemgetter(1))
>>>L
>>>[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
實例6:(DSU方法:Decorate-Sort-Undercorate)
>>>L = [('b',2),('a',1),('c',3),('d',4)]
>>>A = [(x[1],i,x) for i,x in enumerate(L)] #i can confirm the stable sort
>>>A.sort()
>>>L = [s[2] for s in A]
>>>L
>>>[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
以上給出了6中對List排序的方法,其中實例3.4.5.6能起到對以List item中的某一項
為比較關鍵字進行排序.
效率比較:
cmp < DSU < key
通過實驗比較,方法3比方法6要慢,方法6比方法4要慢,方法4和方法5基本相當
多關鍵字比較排序:
實例7:
>>>L = [('d',2),('a',4),('b',3),('c',2)]
>>> L.sort(key=lambda x:x[1])
>>> L
>>>[('d', 2), ('c', 2), ('b', 3), ('a', 4)]
我們看到,此時排序過的L是僅僅按照第二個關鍵字來排的,如果我們想用第二個關鍵字
排過序後再用第一個關鍵字進行排序呢?有兩種方法
實例8:
>>> L = [('d',2),('a',4),('b',3),('c',2)]
>>> L.sort(key=lambda x:(x[1],x[0]))
>>> L
>>>[('c', 2), ('d', 2), ('b', 3), ('a', 4)]
實例9:
>>> L = [('d',2),('a',4),('b',3),('c',2)]
>>> L.sort(key=operator.itemgetter(1,0))
>>> L
>>>[('c', 2), ('d', 2), ('b', 3), ('a', 4)]
為什麼實例8能夠工作呢?原因在於tuple是的比較從左到右之一比較的,比較完第一個,如果
相等,比較第二個

2011年5月2日 星期一

python 3的特性

http://www.ibm.com/developerworks/cn/linux/l-python3-1/
http://www.ibm.com/developerworks/cn/linux/l-python3-2/
老實說,即使到了現在,很多開發的程式還是停留在2.6或者2.7,很多開發者不願意移植,事實上這種轉移比商業軟體還久,所以才在這裡再重複了一個參考連結

2011年4月20日 星期三

Collection內的方法

身為collection的class有許多方法,其實是用來支援某些運算子的
__contains__(self,x) # x in collection
__delitem__(self,k) #del y[k]
__getitem__(self,k) #y[k]
__setitem__(self,k,v)#y[k]=v
__iter__(self) # 回傳iterator
__len__(self) #len(y)
__reversed__(self) #revsersed(y)
理解了這些類似operator overloading的東西之後,大致上對於python就比較能理解背後運作的原理了

2011年4月18日 星期一

Python -- property

先來談談property,我第一次看到類似property的東西是在delphi上面,他讓programmer可以作一些存取控制,但是有如同直接存取變數一樣
接下來java bean也再看到了一次這樣的東西,很不幸的這次是一堆getter跟setter,也就是你必須在屬性前面加上getArea, setArea等等,即使他只是單純的access一個變數,如果沒有IDE的幫助,這簡直是一個惡夢

python上面的作法是使用decorator,也就是用@property這個語法,他支援四種引數,getter, setter,deleter, docstring
可以想像得到,如果使用一個自動計算面積的circle class,如果希望直接取得屬性,如circle.area,你可以使用
@property
def area(self):
return math.pi*(self.radius**2)
如果是setter則是
@property
def radius(self):
return self.__radius
@property.setter
def radius(self,radius):
assert radius>0, "radius must be non zero and non-negative!"
self.__radius=radius
由於property的作用,不用擔心radius這個屬性會產生名稱衝突,其實一使用property的時候,四個屬性等於全部被啟用,但是預設由python接手沒寫得東西,但是python就是啥都不做XD

回過頭來說到屬性,很多時候真的覺得他只是一個candy,也就是甜頭啦,如果說老是充斥這種東西,方便性取代了程式的易讀性以及可能會忽略掉一些程式的邏輯
當一個programmer讀到某個function,他會心裡開始描述這個function的運作方式,他也的確知道這個function出現的時機,對於理解程式是有正面的幫助的,但是因為property的作用,使用者可能會無法領悟到這部份的事實
接下來另外一點干擾是邏輯性,當使用變數的時候,你並不會預期他會丟出一個例外,可是這對很多function來說是很正常的事情,甚至是必要的,在這樣的狀況下,很多使用者會忽略這個部份

老實說,綜觀這麼多程式,我很少可以找到一個絕對的必要性,必須使用property,如果是簡單的access,大可讓使用者直接access,如果必須作一些boundary控制,我覺得應該使用function模式,讓使用者在使用的時候有所警惕

另外其實decorator在python裡面有其他神奇的作用,java也是,decorator感覺已經有點變成語法了orz,另外一個討厭decorator的地方是,感覺他很不像"語法",像是註解,但是又有程式的"邏輯"/"作用"在,decorator其他部份以後有時間再寫

2011年4月16日 星期六

Python心得

本來是打算學習ruby,但是後來傾向python,但是因為本身也不是對任一程式語言沒有接觸過,所以並不打算從頭到尾介紹一次某種程式語言,所以雖然這個網誌算是我的筆記,但也不是適合完全的新手來看,應該比較打算朝向tip or cookbook的方式去紀錄
再者另外一點我覺得很重要的是,我認為python並不適合一個完全不懂程式語言的人去使用,雖然本身它提供了很多syntax candy,讓一些使用者似乎可以很快的進入狀況,但是後面的原理卻是大量的OOP以及泛型。
換句煥說,當一個新手可能很快的進入狀況之後便碰到了一個玻璃的天花板,本身要去學會其他programming的準則或者原理之後才能更進一步。
最後,我認為python很適合拿來實作一些想法,讓使用者更專心在他想驗證或者實作的小功能上,而不是花太多的時間在debug與tuning上面;但是如果是以效能為主,python可能就無法勝任,比方說繁雜的科學計算,當然有lib可以做到這一點,但是他底層是C++ or fortran,主要是因為python的執行效能不彰,python的運算速度的確是慢於C++/java之類的。反過來說,如果是ㄧ些管理功能上的小程式,我倒是覺得python開發遠遠快於C++/java

Python -- OO

Python雖然不全面支援OO所有的特色,並不表示他無法做OO設計,同時也不表示OO不好,雖然我在寫小程式(1k lines以下)覺得Python開發速度很快,但也不表示OO不適用
在許多OOP裡面的overloading以及access control在Python並不支援,大多數Python的的類別屬性是公開的,對於存取的限制仰賴於programmer的素養(依循一定的規則)以及一些decorator的幫助。
另外由於python並不強調型態的重要性,他使用的是duck typing,也就是如果一個型態表現類似某一種型態的特性,你就可以把它當作該型態使用,比方說某隻鳥類似鴨子,你就可以把這隻鳥當作鴨子,可能他只是會呱呱叫,然後會飛短短的距離,python就當他是鴨子,可是根本的型態可能不是鴨子。
python跟java一樣,預設有一個class Object,所有新的class都繼承自這個Object,其中有許多重要的方法,一般python內定的方法使用__(雙底線/double under line)開頭。__str__()傳回一個該物件的字串描述,很類似java的toString();另外有一個__repr__()他回傳該物件的字串,如果執行該字串就可以得到一個一模一樣的物件;預設中python會讓所有的物件可以hash,依賴的是object id,但是一旦改寫了__eq__()方法之後,這個物件就不再是可hash,也就是無法放入set或者dict型態內了。

2011年4月15日 星期五

Python -- Iterable

在python裡面有個重要的類別或者說觀念,就是iterable,何謂iterable?也就是本身要提供__iter__()這個方法,他會回傳一個iterator,每個iterator都有個__next__()的方法來取得下個元素
iterator大量使用在for迴圈跟list/set comprenhesion,對於sequence,iterator將會參考到sequence object,然後在__next__()呼叫sequence.__getitem(index),iterator本身只要維護那個index,就可以從0走到len(sequence),逐一取得sequence裡面的元素
而對於類似dict或者set這類的資料型態,就無法保證取出的順序,更有趣的是dict是屬於key/value的pair,那麼__iter__()回傳的是啥?答案是他是一個由dict.keys()產生的iterator,也就是這個iterator每次呼叫__next__()取回的是下一個key
iterator骨子裡面都是包含資料的物件嗎?答案是有例外的,比方說range所產生的iterator,其實他是一個特殊的yeild關鍵字所產生的iterator,他本身只有一個變數,類似C裡面的static,好處是可以這樣的一個物件不用佔用大量的空間,過去range回傳是一個tuple or list,也就是裡面每個元素都是佔有實際記憶體空間的

回頭來看過去for等等的語法格式
for variable in iterable:
suite
list/set的comprehension格式
[item for item in iterable]
這樣就比較瞭解,我們可以把何種物件變成list或者如何用for迴圈去走訪一個物件裡面的元素

另外有些常用的運算如all(), any(), max(), min(), sort(), sum()等等運算可以作用在iterator之上

其實python裡面還有許多方法是對應到operator overloading跟value的assign,瞭解這些方法,會讓programmer更加瞭解python裡面運作的方式,進而寫出更簡潔的code。

同時如果理解許多本來C/C++並不支援的機制,如decorator,就會更加瞭解python與C/C++的差異。所以我說懂得一種程式語言不是一兩個月內的事情,如果一兩個月內可以學會一種語言,大概就是指語法之類的吧!不然此人必定對程式語言設計本身有極高的素養,才可能在短時間內達成,不然大多數人應該只能花更長的時間理解一個程式語言的內含吧

2011年4月13日 星期三

邏輯、Logic、邏輯、Logic、邏輯、Logic

想來以前還真的沒腦袋XD如何說沒腦袋呢?也就是對寫出來的東西並沒有經過安排,好類似家說的spaghetti code。設計程式好比在建築一般,如果只是不要倒塌,或者設計出一個功能有的系統,就會做得很類似堆砌的工作,好比一個房子建好了,該有的功能都有了,但是用起來就是不順手,外觀就是不好看。

pattern一詞來自建築業,其實當設計師在設計房屋的時候不只考慮到單一的問題,比方說洗手間需要通風良好的地方這個pattern,如果說單單為了這個理由隨便把它放在玄關附近,那整個房子就相當的不倫不類了,一進門就看到洗手間的房子,訪客及主人一進門就聞到洗手間的味道,還真的是不好的印象。所以設計師可能就要考慮把洗手間挪到其他的地方,讓一個問題解決的pattern不會影響到其他的pattern才是整體設計的最高指標。雖然有時無法完全排除,但是總要找到一個相對較好的解決方案。

一個房子該有的功能有了,是最基本的要求,相對於此,整體完整性的設計是更高的標竿,然而整體後面支撐的就是邏輯性、合理性。我過去總是沒有腦袋的把所有問題解決就好,從來沒有思考過如何讓整體得到更好的解決,因為我很少去review自己寫過的東西。

自從看到約耳的文章,認為把code丟掉重新撰寫是一個浪費時間的行為,跟某個日本作者提到,寫下來的文章可以不斷地修改,精煉其中的語文,除了責任感之外更是一種工藝的表現,讓我十分汗顏,我想到我過去丟掉過無數的code,很少對自己的文章重新檢視,以後得好好地加強這個部分,這樣才能做一個好的designer而不是單單的programmer

選擇Python

Python在國內完全比不上國外的發展,幾乎大多數的程式語言不是主流的C#/C++/C跟java就是PHP(web),在不然就是VB,國內的Python資源幾乎是非常缺乏

一開始接觸python是想要在ruby與其中挑選一個,一個script語言,我的要求是可以快速驗證一些我想處理的資料跟想法,而一開始挑選python跟ruby老實說是有點被"騙"了XD,就是網路上那種十分鐘快速建立blogger,我很好奇,如何在十分鐘內完成blogger的coding??結果就是天下沒有白吃的午餐

那麼為何會放棄ruby而選python??因為接觸了rubby一陣子之後,發現他是蠻物件導向的語言,我選擇script就是想輕裝上陣,如果還要考慮物件導向,那麼不就很累人了
難道是OO不好嗎?不是這麼說,瞭解OO, pattern這類的東西,大多是把本來coding的複雜度,轉換為關係的複雜度,只要瞭解到了,你付出了怎樣的成本,獲得怎樣的好處,適度使用OO,也是可以相當的愉快的

python並沒有很完整的支援物件導向,他使用了其他的方式來支援,也就是不是原生的支援物件導向,這點使得他在開發物件導向的程式會受到好些限制,不過同時也換來了相當多的彈性。另外一點不同的是python更強調泛型演算法的部份,他的各種容器幾乎都以泛型的設計為主

我想不少programmer跟我有一樣的"幻覺",認為只要熟悉C/C++之後,其他程式語言上手就很快了,我想這只是語法部分,至於語言中間設計的方式可能要花上三到六個月才能體會出來。如果一個C++換到java很快會發現,他開發速度不如一個java老鳥的人,他或許會解釋為對java library的不熟悉。可是很多不只是java library的問題,比方說code的簡潔程度,code撰寫方式,很多本身是依賴於程式的特性的,更貼近的一點的,比方java有seriable介面來處理物件轉成檔案的問題,可是C++並沒有,這時候C++的programmer可能會自己處理,這並不是表示C++使用者做不出這樣的功能,換了程式語言之後兩者之間生產力的問題。

語言的特色影響了熟悉他與否的programmer生產力,倒不是本身programmer能力的限制,在我慢慢深入python之後,我覺得我會用的程式語言"愈來愈少",因為本身背後設計的原理我能理解大多數的還真的不多!!

2009年7月9日 星期四

Python的基本集合(collection)型態

翻譯作集合(collection)好像蠻弔詭的,因為工程的人很習慣把他們當作set,anyway~
基本上python支援三種(不管是2.6 or 3.0),分別是tuple, list, map

基本上這三種就相當的夠用了,其實也只有兩種,tuple可以看成constant list,不變的list
如果有學過其他程式語言,就更單純了,把list視為list或者動態array,把map當成hash
一切就很自然了

hair = "black", "brown", "blonde", "red"
hair[2]

上面宣告了一個tuple,存取的方式就是使用[]運算子來取得,上面的例子hair[2]就是blonde囉

list基本上跟tuple差不多,差別是,你不能去改變tuple的內容,但是list可以,list有幾種基本運算
宣告空的list就是list=[],夠簡單吧
list.append(x) 在list尾端加入x
list.count(x) 計算list中x元素的個數
list.extend(x) 跟append差不多
list.index(x,start,end) 在[start,end)搜尋元素x,如果找到回傳index,沒找到會丟出例外
list.insert(index,x) 在index之後插入x
list.pop() 取出list最後一個元素,並且回傳他
list.pop(index) 取出list最後一個元素,並且回傳他
list.remove(x) 刪除x元素
list.reverse() 將list反向排列
list.sort() 排序list的內容

如果有基本資料結構的概念就知道,list可以輕易的做到array, list, stack, queue這幾種資料結構
當然,如果你是實際講求效能的,可能就會懷疑[]這運算子是否為O(1)的時間等等,這倒是我不知道的,如果知道的網友,請告訴我,謝謝
那二維陣列呢?多維度陣列呢?這是很幸運與不幸的事情,幸運的是,只要用簡單list在包含list就可以了,比方說list[5]=[],這樣子,同時你也看到一個問題了,他並不保證list元素中是list,也不保證list中的list長度一致,這就是script language的特色吧

最後看到map型態,就把它當作hash table來操作吧,使用{}來宣告空map,裡面的元素key:value使用分號來分開,如同範例
d = {"root": 18, "blue": [75, "R", 2], 21: "venus", -14: None,"mars": "rover", (4, 11): 18, 0: 45}


這裡也再度看到script language的特色,他並不要求你key是相同型態,也不要求value是相同型態,這同時有好處也有壞處
基本的操作有
map.clear() 清空map
map.copy() 複製map
map.items() 取出所有key,value的pair
map.keys() 取出所有的key
map.values() 取出所有的value
通常讀取map會直接用map[key],新增會用map[key]=value
至於拜訪的方式,晚點再寫吧

2009年3月31日 星期二

Python IDE

一直想要有一款輕盈的IDE,當然其中我認為比較成熟的IDE有eric IDE, eclipse+plugin, NetBeans+plugin,有不少IDE是基於python的,現階段python 3.0剛推出,很多IDE因為這樣不能用orz

eric IDE要編譯QT(M$下)很麻煩,eclispe跟Netbeans啟動都不算快,所以囉,最好找到一款ulipad,感覺還可以,目前正在適應中

2009年3月28日 星期六

Python

最近又開始看重複的看ruby跟python,我實在是個半弔子,我對兩者都不熟悉,不過根據最近的一些觀察,我想我會傾向python,尤其常常在linux底下看到python開發的東西

先說說python吧,這的是一個頗為弔詭的東西,他的語法有強烈的限制性,不過發明的人應該覺得維護性的重要性比較重要,不想要一個語言有千百種寫法。另外一個印像是他有眾多lib,這一點我蠻喜歡的。另外一個詭異的是,這麼不錯的一個語言,竟然缺乏一個良好的IDE,簡直叫人難以接受,一個功能比較齊全的IDE竟然要自行編譯QT等等的才能在M$底下執行(在linux底下就簡單多了)。再加上目前是2.0跨向3.0(很多東西不相容),所以資料跟一些lib有點銜接不上來。種種的現象都叫我很不能"接受"

再回頭看看ruby,他class定義的確顯的蠻幽雅(看看那些java的reader/writer method真的有點orz),也有不錯的IDE,可是他的lib相對於python比較少,資料結構的資源好像也不是那麼多,可是ruby on rails這部分又是比python好。

兩相比較下,python給我的驚奇比ruby多,因為一些功能性上來說,python顯的比較完備(我想拿python來做些簡單程式開發,不大需要RoR的東西,所以Ruby的吸引力比較小),於是我決定進入python的世界,可是如果單單就受歡迎性,ruby因為有RoR的加持,再加上目前很多人再開發web application,ruby聲勢顯的比python大,這也有可能造成以後ruby補完之後擠掉python,呵呵

會想要學習python之類的東西也很簡單,因為目前硬體已經發展到這種程度,程式的開發時間遠大於執行時間,如果在單單驗證想法跟一些簡單的功能上面,script language已經比C/java之類來的簡單。或許python之類的執行能力比C/java之類來的差,可是開發可即刻執行的優點超越了他們。另外script language有個很大的缺點就是,大多沒有特定的GUI,這會讓開發應用程式上面有蠻大的"障礙",因為等於要去銜接某種GUI lib,這在開發上比較吃虧(這一點java就做的很好)

anyway~沒有哪種語言比較好,只是有時殺雞不用牛刀,挑一種適合的程式語言去解決人們碰到的問題才是王道~呵呵

2008年12月30日 星期二

Linux Kernel核心中文手冊

這是一個作者不詳的文件,如果有人知道的話,歡迎通知我,讓我補上,另外這是一份介紹性的文件
==================================================
  Chapter 1

 

Hardware Basic(硬件基礎知識)

 

一個操作系統必須和作為它的基礎的硬件系統緊密配合。操作系統需要使用一些只有硬件才能提供的功能。為了完整的瞭解Linux,你需要瞭解底層硬件的基礎知識。本章對於現代PC的硬件進行了。

1975年1月「Popular Electronics」雜誌封面上印出了Altair 8080的圖片,一場革命開始了。

Altair 8080,跟隨早期的「Star Trek epsode」命名,只需要$397,就可由個人電子愛好者自己組裝。它擁有Intel 8080處理器和256字節內存,但是沒有屏幕和鍵盤。以今天的標準來衡量,它太簡陋了。它的發明者,Ed Roberts,製造了名詞「personal computer「來命名他的發明,但現在,PC這個名詞已經用來命名幾乎所有你可以不依靠幫助就可以自己運行起來的計算機。用這個定義,甚至一些十分強 大的Alpha AXP系統也是PC。

愛好者們看到了Altair的潛力,開始為它寫軟件,製造硬件。對於這些早期的先驅來講,它代表著自由:從被神職人員控制和運行的大型批處理的主機 系統中逃脫出來的自由。你可以在自己家裡甚至廚桌上擁有計算機,這使學院的退學生為此著迷並通宵達旦。與此同時出現大量硬件,在一定程度上各自不同,而軟 件專家則樂於為這些新機器撰寫軟件。有諷刺意味的是,IBM在1981年發佈了IBM PC並於1982年早期供貨,從此定義了現代PC的模型。它擁有Intel 8088處理器,64K內存(可以擴充到256K),兩個軟驅和一個80x25的彩色圖卡(CGA),用今天的標準衡量,它功能不算很強,但是它銷售的不 錯。1983年,緊接著推出的IBM PC-XT,則擁有一個豪華的10M硬盤。不久大批公司如Compaq開始製造IBM PC的複製品,PC的結構成為了事實的標準。這個事實的標準使大批硬件公司可以在這個不斷增長的市場上一起競爭,反過來,可以遏制價格,讓用戶滿意。現代 PC承襲了早期PC的許多系統體系特徵。甚至基於最強大的Intel Pentium Pro的系統也可以運行Intel 8086的尋址模式。當Linus Torvalds開始開發後來的Linux時,他選擇了當時最常見和價格最合理的硬件平台:一台Intel 80386 PC。

從PC的外面看,最明顯的部件就是機箱、鍵盤、鼠標和顯示器。在機箱的前面有一些按鈕,一個小屏幕顯示一些數字,還有一個軟驅。現在的大多數系統還有一個CD-ROM期、驅動器。如果你需要保護你的數據,那麼還會有一個備份用的磁帶機。這些設備一律被看作外設。

雖然CPU管理整個系統,但它並不是唯一的智能設備。所有的外設控制器,例如IDE控制器,也都擁有一定程度的智能。在PC內部(圖1.1),你可 以看到一個主板,包括CPU或微處理器、內存和一些ISA或PCI外設控制卡的槽位。其中一些控制器,如IDE磁盤控制器可能內置在系統主板上。

 


CPU

 

CPU,或者說微處理器,是所有計算機系統的心臟。微處理器進行數學運算,邏輯操作並從內存中讀取指令並執行指令,進而控制數據流向。計算機發展的 早期,微處理器的各種功能模塊是由相互分離(並且尺寸上十分巨大)的單元構成。這也是名詞「中央處理單元」的起源。現代的微處理器將這些功能模塊集中在一 塊非常小的矽晶片製造的集成電路上。在本書,名詞CPU、微處理器和處理器交替使用。 微處理器處理二進制數據:這些數據由1和0組成。這些1和0對應電氣開關的開或關。就好像42代表4個10和2個單元,二進制數字由一系列代表2的冪數的 數字組成。這裡,冪數意味著一個數字用自身相乘的次數。10 的一次冪是10,10的2次冪是10x10,10的3次冪是10x10x10,依此類推。二進制0001是十進制1是十進制2,二進制0011是十進制 3,二進制0100是十進制4,等等。所以,十進制42是二進制101010或者(2+8+32或21+23+25)。在計算機程序除了使用二進製表示數 字之外,另一種基數,16進制,也經常用到。在這種進制中,每一位數字表示16的冪數。因為十進制數字只是從0到9,在十六進制中10到15分別用字母 A,B,C,D,E,F表示。例如,十六進制的E是十進制的14,而十六進制的2A是十進制的42(2個16+10)。用C語言的表示法(本書一直使 用),十六進制數字使用前綴「0x」:十六進制的2A寫做0x2A。

 

微處理器可以執行算術運算如加、乘和除,也可以執行邏輯操作例如「X是否大於Y」。

處理器的執行由外部時鍾控制。這個時鍾,即系統時鍾,對處理器產生穩定的時鍾脈衝,在每一個時鍾脈衝裡,處理器執行一些工作。例如,處理器可以在每 一個時鍾脈衝裡執行一條指令。處理器的速度用系統時鍾的頻率來描述。一個100Mhz的處理器每秒鍾接受到100,000,000次時鍾脈衝。用時鍾頻率 來描述CPU的能力是一種誤解,因為不同的處理器在每一次時鍾脈衝中執行的工作量不同。雖然如此,如果所有的條件同等,越快的時鍾頻率表示處理器的能力越 強。處理器執行的指令非常簡單,例如:「把內存位置X的內容讀到寄存器Y中「。寄存器是微處理器的內部存儲空間,用來存儲數據並進行操作。執行的操作可能 使處理器停止當前操作而轉去執行內存中其他地方的指令。正是這些微小的指令集合在一起,賦予現代的微處理器幾乎無限的能力,因為它每秒可以執行數百萬甚至 數十億的指令。

 

執行指令時必須從內存中提取指令,指令自身也可能引用內存中的數據,這些數據也必須提取到內存中並在需要的時候保存到內存中去。

 

一個微處理器內部寄存器的大小、數量和類型完全決定於它的類型。一個Intel 80486處理器和一個Alpha AXP處理器的寄存器組完全不同。另外,Intel是32位寬而Alpha AXP是64位寬。但是,一般來講,所有特定的處理器都會有一些通用目的的寄存器和少量專用的寄存器。大多數處理器擁有以下特殊用途的專用的寄存器:

 

Program Counter(PC)程序計數器

這個寄存器記錄了下一條要執行的指令的地址。PC的內容在每次取指令的時候自動增加。

Stack Pointer(SP)堆棧指針

處理器必須能夠存取用於臨時存儲數據的大容量的外部讀寫隨機存取內存(RAM)。堆棧是一種用於在外部內存中存放和恢復臨時數據的方法。通常,處理 器提供了特殊的指令用於將數據壓在堆棧中,並在以後需要是取出來。堆棧使用LIFO(後進先出)的方式。換句話說,如果你壓入兩個值x和y到堆棧中,然後 從堆棧中彈出一個值,那麼你會得到y的值。

一些處理器的堆棧向內存頂部增長,而另一些向內存的底部增長。還有一些處理器兩種方式都可以支持,例如:ARM。

 

Processor Status(PS)

指令可能產生結果。例如:「X寄存器的內容是否大於Y寄存器的內容?「可能產生真或假的結果。PS寄存器保留這些結果以及處理器當前狀態的其他信息。多數處理器至少有兩種模式:kernel(核心態)和user(用戶態),PS寄存器會紀錄能夠確定當前模式的那些信息。

 


Memory(內存)

 

所有系統都具有分級的內存結構,由位於不同級別的速度和容量不同的內存組成。

最快的內存是高速緩存存儲器,就像它的名字暗示的一樣-用於臨時存放或緩存主內存的內容。這種內存非常快但是比較昂貴,因此多數處理器芯片上內置有 少量的高速緩衝存儲器,而大多數高速緩存存儲器放在系統主板上。一些處理器用一塊緩存內存同時緩存指令和數據,而另一些處理器有兩塊緩存內存-一個用於指 令,另一個用於數據。Alpha AXP處理器有兩個內置的內存高速緩存存儲器:一個用於數據(D-Cache),另一個用於指令(I-Cache)。它的外部高速緩衝存儲器(或B- Cache)將兩者混在一起。

最後一種內存是主內存。相對於外部高速緩存存儲器而言速度非常慢,對於CPU內置的高速緩存存儲器,主內存簡直是在爬。

高速緩存存儲器和主內存必須保持同步(一致)。換句話說,如果主內存中的一個字保存在高速緩存存儲器的一個或多個位置,那麼系統必須保證高速緩存存 儲器和主內存的內容一樣。使高速緩衝存儲器同步的工作一部分是由硬件完成,另一部分則是由操作系統完成的。對於其它一些系統的主要任務,硬件和軟件也必須 緊密配合。

 


Buses(總線)

系統板的各個組成部分由被稱為總線的連接系統互連在一起。系統總線分為三種邏輯功能:地址總線、數據總線和控制總線。地址總線指定了數據傳輸的內存 位置(地址),數據總線保存了傳輸的數據。數據總線是雙向的,它允許CPU讀取,也允許CPU寫。控制總線包含了各種信號線用於在系統中發送時鍾和控制信 號。有許多種不同的總線類型,ISA和PCI總線是系統用於連接外設的常用方式。

 


Controllers and Peripherals (控制器和外設)

 

外設指實在的設備,如由系統板或系統板插卡上的控制芯片所控制的圖形卡或磁盤。IDE控制芯片控制IDE磁盤,而SCSI控制芯片控制SCSI磁 盤。這些控制器通過不同的總線連接到CPU並相互連接。現在製造的大多數系統都是用PCI或ISA總線將系統的主要部件連接在一起。控制器本身也是象 CPU一樣的處理器,它們可以看作CPU的智能助手,CPU擁有系統的最高控制權。

 

所有的控制器都是不同的,但是通常它們都有用於控制它們的寄存器。CPU上運行的軟件必須能夠讀寫這些控制寄存器。一個寄存器可能包含描述錯誤的狀 態碼,另一個寄存器可能用於控制用途,改變控制器的模式。一個總線上的每一個控制器都可以分別被CPU尋址,這樣軟件設備驅動程序就可以讀寫它的寄存器進 而控制它。IDE電纜是一個好例子,它給了你分別存取總線上每一個驅動器的能力。另一個好例子是PCI總線,允許每一個設備(如圖形卡)被獨立存取。

 


Address Spaces(尋址空間)

 

連接CPU和主內存的系統總線以及連接CPU和系統硬件外設的總線是分離的。硬件外設所擁有的內存空間稱為I/O空間。I/O空間本身可以再進一步 劃分,但是我們現在先不討論。CPU可以訪問系統內存空間和I/O空間,而控制器只能通過CPU間接訪問系統內存。從設備的角度來看,比如軟驅控制器,它 只能看到它的控制寄存器所在的地址空間(ISA),而非系統內存。一個CPU用不同的指令去訪問內存和I/O空間。例如,可能有一條指令是「從I/O地址 0x3f0讀取一個字節到X寄存器「。這也是CPU通過讀寫系統硬件外設處於I/O地址空間的寄存器從而控制外設的方法。在地址空間中,普通外設(如 IDE控制器,串行端口,軟驅控制器等等)的寄存器在PC外設的多年發展中已經成了定例。I/O空間的地址0x3f0正是串行口(COM1)的控制寄存器 的地址。

 

有時控制器需要直接從系統內存讀取大量內存,或直接寫大量數據到系統內存中去。比如將用戶數據寫到硬盤上去。在這種情況下,使用直接內存存取(DMA)控制器,允許硬件設備直接存取系統內存,當然,這種存取必須在CPU的嚴格控制和監管下進行。

 


Timer(時鍾)

所有操作系統需要知道時間,現代PC包括一個特殊的外設,叫做實時時鍾(RTC)。它提供了兩樣東西:可靠的日期和精確的時間間隔。RTC有自己的 電池,所以即使PC沒有加電,它仍在運行。這也是為什麼PC總是「知道」正確的日期和時間。時間間隔計時允許操作系統精確地調度基本工作。

 

 

Chapter 2

 

Software Basic(軟件基礎)

程序是用於執行特定任務的計算機指令組合。程序可以用彙編語言,一種非常低級的計算機語言來編寫,也可以使用和機器無關的高級語言,比如C語言編 寫。操作系統是一個特殊的程序,允許用戶通過它運行應用程序,比如電子錶和文字處理等等。本章介紹了基本的編程原理,並簡介操作系統的目的和功能。

 

2.1 Computer Languages(計算機語言)

 

2.1.1.彙編語言

 

CPU從內存中讀取和執行的指令對於人類來講無法理解。它們是機器代碼,精確的告訴計算機要做什麼。比如十六進制數0x89E5,是Intel 80486的指令,將寄存器ESPEBP中。早期計算機中最初的軟件工具之一是彙編程序,它讀入人類可以閱讀的源文件,將其裝配成機器代碼。彙編語言明確 地處理對寄存器和對數據的操作,而這種操作對於特定的微處理器而言是特殊的。Intel X86微處理器的彙編語言和Alpha AXP微處理器的彙編語言完全不同。以下Alpha AXP彙編代碼演示了程序可以執行的操作類型:

 

Ldr r16, (r15) ; 第一行

Ldr r17, 4(r15) ; 第二行

Beq r16,r17,100; 第三行

Str r17, (r15); 第四行

100: ; 第五行

 

第一條語句(第一行)將寄存器15指定的地址中的內容加載到寄存器16中。第二條指令將緊接著的內存中的內容加載到寄存器17中。第三行比較寄存器 16和寄存器17,如果相等,分支到標號100,否則,繼續執行第四行,將寄存器17的內容存到內存中。如果內存中的數據相同,就不必存儲數據。編寫彙編 級的程序需要技巧而且十分冗長,容易出錯。Linux系統的核心很少的一部分是用彙編語言編寫,而這些部分之所以使用彙編語言只是為了提高效率,並且和具 體的微處理器相關。

 

2.1.2 The C Programming Language and Compiler (C語言和編譯器)

 

使用彙編語言編寫大型程序十分困難,消耗時間,容易出錯而且生成的程序不能移植,只能束縛在特定的處理器家族。更好的選擇是使用和機器無關的語言, 例如C。C允許你用邏輯算法描述程序和要處理的數據。被稱為編譯程序(compiler)的特殊程序讀入C程序,並將它轉換為彙編語言,進而產生機器相關 的代碼。好的編譯器生成的彙編指令可以和好的彙編程序員編寫的程序效率接近。大部分Linux核心是用C語言編寫的。以下的C片斷:

if (x != y)

x = y;

執行了和前面示例中彙編代碼完全一樣的操作。如果變量x的內容和變量y的內容不一樣,變量y的內容被拷貝到變量x。C代碼用例程(routine) 進行組合,每一個例程執行一項任務。例程可以返回C所支持的任意的數值或數據類型。大型程序比如Linux核心分別由許多的C語言模塊組成,每一個模塊有 自己的例程和數據結構。這些C源代碼模塊共同構成了邏輯功能比如文件系統的處理代碼。

 

C支持多種類型的變量。一個變量是內存中的特定位置,可用符號名引用。上述的C片斷中,x和y引用了內存中的位置。程序員不需要關心變量在內存中的具體位置,這是連接程序(下述)必須處理的。一些變量包含不同的數據例如整數、浮點數等和另一些則包含指針。

 

指針是包含其它數據在內存中的地址的變量。假設一個變量x,位於內存地址0x80010000, 你可能有一個指針px,指向x。 Px可能位於地址0x80010030。Px的值則是變量x的地址,0x80010000。

 

C允許你將相關的變量集合成為結構。例如:

Struct {

Int I;

Char b;

} my_struct;

是一個叫做my_struct的數據結構,包括兩個元素:一個整數(32位)I和一個字符(8位數據)b。

 

2.1.3 Linkers(連接程序)

 

連接程序將幾個目標模塊和庫文件連接在一起成為一個單獨的完整程序。目標模塊是彙編程序或編譯程序的機器碼輸出,它包括機器碼、數據和供連接程序使 用的連接信息。比如:一個目標模塊可能包括程序的所有數據庫功能,而另一個目標模塊則包括處理命令行參數的函數。連接程序確定目標模塊之間的引用關係,即 確定一個模塊所引用的例程和數據在另一個模塊中的實際位置。Linux核心是由多個目標模塊連接而成的獨立的大程序。

 

2.2 What is an Operating System(什麼是操作系統?)

 

沒有軟件,計算機只是一堆發熱的電子元件。如果說硬件是計算機的心臟,則軟件就是它的靈魂。操作系統是允許用戶運行應用程序的一組系統程序。操作系 統將系統的硬件抽象,呈現在用戶和應用程序之前的是一個虛擬的機器。是軟件造就了計算機系統的特點。大多數PC可以運行一到多個操作系統,而每一個操作系 統從外觀和感覺上都大不相同。Linux由不同功能的部分構成,這些部分總體組合構成了Linux操作系統。Linux最明顯的部分就是Kernel自 身,但是如果沒有shell或libraries一樣沒有用處。

 

為了瞭解什麼是操作系統,看一看在你輸入最簡單的命令時發生了什麼:

 

$ls

Mail c images perl

Docs tcl

$

這裡的$是登錄的shell輸出的提示符(此例是bash):表示shell在等候你(用戶)輸入命令。輸入ls引發鍵盤驅動程序識別輸入的字符, 鍵盤驅動程序將識別的字符傳遞給shell去處理。shell先查找同名的可執行映像,它找到了/bin/ls, 然後調用核心服務將ls執行程序加載到虛擬內存中並開始執行。ls執行程序通過執行核心的文件子系統的系統調用查找文件。文件系統可能使用緩存的文件系統 信息或通過磁盤設備驅動程序從磁盤上讀取文件信息,也可能是通過網絡設備驅動程序同遠程主機交換信息而讀取本系統所訪問的遠程文件的詳細信息(文件系統可 以通過NFS網絡文件系統遠程安裝)。不管文件信息是如何得到的,ls都將信息輸出,通過顯示驅動程序顯示在屏幕上。

 

以上的過程看起來相當複雜,但是它說明了即使是最簡單的命令也是操作系統各個功能模塊之間共同協作的結果,只有這樣才能提供給你(用戶)一個完整的系統視圖。

 

2.2.1 Memory management(內存管理)

 

如果擁有無限的資源,例如內存,那麼操作系統所必須做的很多事情可能都是多餘的。所有操作系統的一個基本技巧就是讓少量的物理內存工作起來好像有相 當多的內存。這種表面看起來的大內存叫做虛擬內存,就是當軟件運行的時候讓它相信它擁有很多內存。系統將內存分為容易處理的頁,在系統運行時將這些頁交換 到硬盤上。而應用軟件並不知道,因為操作系統還使用了另一項技術:多進程。

 

2.2.2 Processes (進程)

 

進程可以看作一個在執行的程序,每一個進程都是正在運行的特定的程序的獨立實體。如果你觀察一下你的Linux系統,你會發現有很多進程在運行。例如:在我的系統上輸入ps 顯示了以下進程:

$ ps

PID TTY STAT TIME COMMAND

158 pRe 1 0:00 -bash

174 pRe 1 0:00 sh /usr/X11R6/bin/startx

175 pRe 1 0:00 xinit /usr/X11R6/lib/X11/xinit/xinitrc --

178 pRe 1 N 0:00 bowman

182 pRe 1 N 0:01 rxvt -geometry 120x35 -fg white -bg black

184 pRe 1 <>

185 pRe 1 <>

187 pp6 1 9:26 /bin/bash

202 pRe 1 N 0:00 rxvt -geometry 120x35 -fg white -bg black

203 ppc 2 0:00 /bin/bash

1796 pRe 1 N 0:00 rxvt -geometry 120x35 -fg white -bg black

1797 v06 1 0:00 /bin/bash

3056 pp6 3 <>

3270 pp6 3 0:00 ps

$

 

如果我的系統擁有多個CPU那麼每個進程可能(至少在理論上如此)都在不同的CPU上運行。不幸的是,只有一個,所以操作系統又使用技巧,在短時間 內依次運行每一個進程。這個時間段叫做時間片。這種技巧叫做多進程或調度,它欺騙了每一個進程,好像它們是唯一的進程。進程相互之間受到保護,所以如果一 個進程崩潰或不能工作,不會影響其他進程。操作系統通過給每一個進程一個獨立的地址空間來實現保護,進程只能訪問它自己的地址空間。  

2.2.3 Device Drivers(設備驅動程序)

 

設備驅動程序組成了Linux核心的主要部分。象操作系統的其他部分一樣,它們在一個高優先級的環境下工作,如果發生錯誤,可能會引發嚴重問題。設 備驅動程序控制了操作系統和它控制的硬件設備之間的交互。比如:文件系統向IDE磁盤寫數據塊是使用通用塊設備接口。驅動程序控制細節,並處理和設備相關 的部分。設備驅動程序和它驅動的具體的控制器芯片相關,所以,如果你的系統有一個NCR810的SCSI控制器,那麼你需要NCR810的驅動程序。

 

2.2.4 The Filesystems(文件系統)

 

象Unix一樣,在Linux裡,系統對獨立的文件系統不是用設備標示符來存取(比如驅動器編號或驅動器名稱),而是連接成為一個樹型結構。 Linux在安裝新的文件系統時,把它安裝到指定的安裝目錄,比如/mnt/cdrom,從而合併到這個單一的文件系統樹上。Linux的一個重要特徵是 它支持多種不同的文件系統。這使它非常靈活而且可以和其他操作系統良好共存。Linux最常用的文件系統是EXT2,大多數Linux發佈版都支持。

 

文件系統將存放在系統硬盤上的文件和目錄用可以理解的統一的形式提供給用戶,讓用戶不必考慮文件系統的類型或底層物理設備的特性。Linux透明的 支持多種文件系統(如MS-DOS和EXT2),將所有安裝的文件和文件系統集合成為一個虛擬的文件系統。所以,用戶和進程通常不需要確切知道所使用的文 件所在的文件系統的類型,用就是了。

 

塊設備驅動程序掩蓋了物理塊設備類型的區別(如IDE和SCSI)。對於文件系統來講,物理設備就是線性的數據塊的集合。不同設備的塊大小可能不 同,如軟驅一般是512字節,而IDE設備通常是1024字節,同樣,對於系統的用戶,這些區別又被掩蓋。EXT2文件系統不管它用什麼設備,看起來都是 一樣的。

 

2.3 Kernet Data Structures(核心數據結構)

 

操作系統必須紀錄關於系統當前狀態的許多信息。如果系統中發生了事情,這些數據結構就必須相應改變以反映當前的實際情況。例如:用戶登錄到系統中的時候,需要創建一個新的進程。核心必須相應地創建表示此新進程的數據結構,並和表示系統中其他進程的數據結構聯繫在一起。  

這樣的數據結構多數在物理內存中,而且只能由核心和它的子系統訪問。數據結構包括數據和指針(其他數據結構或例程的地址)。乍一看,Linux核心 所用的數據結構可能非常混亂。其實,每一個數據結構都有其目的,雖然有些數據結構在多個的子系統中都會用到,但是實際上它們比第一次看到時的感覺要簡單的 多。

 

理解Linux核心的關鍵在於理解它的數據結構和核心處理這些數據結構所用到的大量的函數。本書以數據結構為基礎描述Linux核心。論及每一個核心子系統的算法,處理的方式和它們對核心數據結構的使用。

 

2.3.1 Linked Lists(連接表)

 

Linux使用一種軟件工程技術將它的數據結構連接在一起。多數情況下它使用鏈表數據結構。如果每一個數據結構描述一個物體或者發生的事件的單一的 實例,比如一個進程或一個網絡設備,核心必須能夠找出所有的實例。在鏈表中,根指針包括第一個數據結構或單元的地址,列表中的每一個數據結構包含指向列表 下一個元素的指針。最後元素的下一個指針可能使0或NULL,表示這是列表的結尾。在雙向鏈表結構中,每一個元素不僅包括列表中下一個元素的指針,還包括 列表中前一個元素的指針。使用雙向鏈表可以比較容易的在列表中間增加或刪除元素,但是這需要更多的內存存取。這是典型的操作系統的兩難情況:內存存取數還 是CPU的週期數。

 

2.3.2 Hash Tables

 

鏈接表是常用的數據結構,但是遊歷鏈接表的效率可能並不高。如果你要尋找指定的元素, 可能必須查找完整個表才能找到。Linux使用另一種技術:Hashing 來解決這種侷限。Hash table是指針的數組或者說向量表。數組或向量表是在內存中依次存放的對象。書架可以說是書的數組。數組用索引來訪問,索引是數組中的偏移量。再來看書 架的例子,你可以使用在書架上的位置來描述每一本書:比如第5本書。

 

Hash table是一個指向數據結構的指針的數組,它的索引來源於數據結構中的信息。如果你用一個數據結構來描述一個村莊的人口,你可以用年齡作為索引。要找出 一個指定的人的數據,你可以用他的年齡作為索引在人口散列表中查找,通過指針找到包括詳細信息的數據結構。不幸的是,一個村莊中可能很多人年齡相同,所以 散列表的指針指向另一個鏈表數據結構,每一個元素描述同齡人。即使這樣,查找這些較小的鏈表仍然比查找所有的數據結構要快。

 

Hash table可用於加速常用的數據結構的訪問,在Linux裡常用hash table來實現緩衝。緩衝是需要快速存取的信息,是全部可用信息的一個子集。數據結構被放在緩衝區並保留在那裡,因為核心經常訪問這些結構。使用緩衝區 也有副作用,因為使用起來比簡單鏈表或者散列表更加複雜。如果數據結構可以在緩衝區找到(這叫做緩衝命中),那麼一切很完美。但是如果數據結構不在緩衝區 中,那麼必須查找所用的相關的數據結構,如果找到,那麼就加到緩衝區中。增加新的數據結構到緩衝區中可能需要廢棄一個舊的緩衝入口。Linux必須決定廢 棄那一個數據結構,風險在於廢棄的可能使Linux下一個要訪問的數據結構。

 

2.3.3 Abstract Interfaces(抽象接口)

 

Linux核心經常將它的接口抽象化。接口是以特定方式工作的一系列例程和數據結構。比如:所有的網絡設備驅動程序都必須提供特定的例程來處理特定 的數據結構。用抽象接口的方式可以用通用的代碼層來使用底層特殊代碼提供的服務(接口)。例如網絡層是通用的,而它由底層符合標準接口的同設備相關的代碼 提供支持。

通常這些底層在啟動時向高一層登記。這個登記過程常通過在鏈接表中增加一個數據結構來實現。例如,每一個連結到核心的文件系統在核心啟動時進行登記 (或者如果你使用模塊,在文件系統第一次使用時向核心登記)。你可以查看文件/proc/filesystems來檢查那些文件系統進行了登記。登記所用 的數據結構通常包括指向函數的指針。這是執行特定任務的軟件函數的地址。再一次用文件系統登記的例子,每一個文件系統登記時傳遞給Linux核心的數據結 構都包括一個和具體文件系統相關的例程地址,在安裝文件系統時必須調用。

2008年11月2日 星期日

Refactoring跟Unit Test

打開refactoring的書皮,他有手法的列表,我看完之後,天阿,是簡單阿,可是...可是...這不是人做的工作阿~~~~~隨便找個程式開始refactoring,很容易發現,你很難在上百個refactoring步驟之後還保證你的程式無誤,瑣碎的步驟本身就是引入bug的危機阿

接著當然作者提出了一個相當有效且正面的手法Unit Test,有了Unit Test你就會有勇氣(很可惜不是保證程式正確,不過我真的感覺到Unit Test跟張網子一樣,你摔下去有人接著,雖然未必接得住XD),有這機制的監控,也比較容易保持程式的正確性;他帶來另外一個好處是,很容易小步驟的修改程式,而非wafterfall的開發手法

C++比較可悲一點,他的refactoring的工具不如java多跟完整,對於java來說,我覺得目前做得相當好的是eclipse(雖然我覺得這工具在寫GUI程式上面蠻陽春的,不要跟我提起他的plug-in,我都裝不起來:P),有了工具終於可以讓程式設計師好好的做設計,不然光重複打字、搬動、重新命名、刪除,我想就有一堆錯誤了。

給將要進入refactoring跟使用unit test的人,請千萬記得做兩件事,一,找到好的工具,二,使用Unit Test ( P.S. 當然先把refactoring跟unit test怎樣使用弄懂)

2008年11月1日 星期六

JUnit4(一)

JUnit4已經發表有一段時間了,藉由JDK5之後的Annotation的幫助,在撰寫unit上面更加的容易了,先來看一個簡單計算機
public class caculator{
private int result;
public caculator(){
result=0;
}
public void add(int n){
result+=n;
}
public void multiply(int n){
}
public void divide(int n){
result/=n;
}
public void substract(int n){
result-=1;
}
public void clear(){
result=0;
}
public int getResult(){
return result;
}
}


上面有幾點要解說的,其中有幾個method是故意做錯的,好用於測試junit,接著看junit4所寫的程式碼

import static org.junit.Assert.*;
import static java.lang.System.out;
import org.junit.Before;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;

public class caculatorTest{
private caculator c;
@Before public void setUp(){
out.println("set up");
c=new caculator();
}
@After public void tearDown(){
out.println("tear down");
c=null;
}
@Test public void testAdd(){
out.println("test add");
c.add(5);
assertEquals(5,c.getResult());
}
@Ignore
@Test public void testMultiply(){
out.println("test multiply");
}
public static junit.framework.Test suite() {
return new junit.framework.JUnit4TestAdapter(caculatorTest.class);
}
}
裡面可以看到out.println是故意加上去,可以用來觀察程式的運作方式,過去以往在每個測試的method前後會被呼叫的setUp跟tearDown可以直接被@Before跟@After所取代,這兩個方法的功能分別是來執行建構準備測試的物件跟把物件清除,這裡單純的只要建構caculator物件跟把他設定為null(即是"刪除"該物件),這樣做的目的確保在測試過程中不會"污染"物件,導致測試結果可能會依賴於測試方法呼叫的次序

@Test這個annotation表示我們要測試某個功能,裡面所要用到的物件分別在@Before跟@After這兩個annotation的方法被準備好了;@Ignore表示說這個方法尚且未被準備好來測試,可能是因為還沒實做,也可能是其他原因

裡面的static method可以回傳一個TestSuite物件,裡面只要加入要測試的class就可以了
將上面兩個檔案放在同一個目錄,並且從junit的官方網站下載junit4.jar放到相同的目錄,接著執行

java -cp %CLASSPATH%;./junit-4.5.jar;. org.junit.runner.JUnitCore caculatorTest

這裡org.junit.runner.JUnitCore這個類別包含了一個main method,他接受的參數為準備要接受測試的class,如果有多個要測試的class就都加入在後面即可
結果如下
JUnit version 4.5
.set up
test add
tear down

Time: 0.015

OK (1 test)

請注意那個紅色的點,每個點表示通過一項測試,後面可以看到在乎叫testadd之前會先呼叫setUp跟之後呼叫tearDown,其實junit4再引入annotation之後,那兩個方法的名稱已經可以隨意取,可是沿用過去junit3以前的慣例比較顯的有可讀性,這裡也有一個跟junit3不一樣的就是,class已經不用強制繼承TestCase這個class了

2008年10月27日 星期一

雜七雜八的名詞解釋

HTPC(Home Theater Personal Computer)
應該考慮的部分是硬體
主要就是解壓縮、介面跟容量
解壓縮最好能夠硬體解壓縮支援,ATI在低階的卡就有些支援,相對的N家的集中在DirectX支援,比較偏向遊戲
介面的話,就是挑有支援一堆啥Full-HD之類的接頭,方便接各種裝置
最後容量的話就沒啥好說的,影片檔案都不小,容量儘量大

PAM

fabless
wiki:無廠半導體公司是專注於設計與銷售應用半導體晶片的硬體裝置並透過將半導體的生產製造外包予專業晶圓代工半導體製造廠商來取得優勢的公司。智霖公司 (Xilinx)的Bernie Vonderschmitt與Chips and Technologies公司的Gordon A. Campbell是無廠概念的先驅者。

無廠半導體公司可將其資源集中在最終市場研發而不需投放資源於保有當前的半導體技術。由於他們的主要產品由專利許可、商業秘密、光罩著作及其他形式的知識產權構成而被稱為知識產權公司(IP firms)。

launchpad
Launchpad 是一個提供維護、支援或聯絡 Ubuntu 開發者的網站平台,由 Ubuntu 的母公司 Canonical 有限公司所架設。
issue:http://wiki.ubuntu-tw.org/index.php?title=Launchpad

mingw, cygwin, xmingw
MinGW 可以唸做 Min-G-W「明~碁~大不了~」或是MinG-W「名~大不了~」,也有人唸做 ming-wee「名貴(台語發音)」 或是 min gnu 「明~格奴」。可參考 http://en.wikipedia.org/wiki/MinGW

MinGW 原來是 Cygwin (念做「賽葛-wing」)裡 GNU 開發工具的一個分支,沒 Cygwin 複雜,而且目的也與 Cygwin 不同,Cygwin 的目的是希望透過一個 dll (就是 Cygwin1.dll 通常被安裝在 C:\Windows\system32底下) 建立一個系統呼叫轉換層,將原先 unix 世界裡的 system call 呼叫,經由這個 DLL 把它們映對到微軟視窗作業系統的系統呼叫,如此一來就可以很方便在 Windows 底下模擬出一個 unix 平台來,這樣你不需要一個 unix 機器照樣也可以開發 unix 程式。開發好的程式只要拿到 UNIX 平台上就可以使用。同理原來UNIX上諸多 Open source 程式也可以粉方便拿到 Windows 世界來享用,當然啦!UNIX system calls 與 Windows APIs 不盡然可以一對一對應,Cygwin 的目標在於儘可能 Compatible,執行績效倒不是重點考量。

「名~大不了~」可就不同啦!他的目的反而是將 gnu 這個 open source, free 的開發工具移植到微軟視窗平台,他直接產生出來的PC視窗平台機器碼,可以說與微軟 Visual Studio 做出來的一摸一樣,可以彼此互相通用。執行效率反而是重點。MinGW 常與 MSYS(Minimal SYStem)搭配使用,後者是跑在Windows視窗上的一個瘦身型羽量級 UNIX shell 功能,MinGW與他搭檔還可以進一步做一些更彈性的 Config。(按: UNIX 世界Open source 的玩法,大約都是用 cvs 或是 Sub version 去取得 Source code,然後 configure --> build --> install 三部曲, configure 會先蒐集你平台上各個工具元件實際安裝路徑,根據它們來調整軟體 Project檔(通常它是一個Makefile),之後才執行 make 去 Build 出軟體,透過 Install 腳本,最後再把作好的軟體 install (安裝)到適當的資料夾來使用。這 MSYS 就可以讓 MinGW 這編譯工具也具備第一步的 configure 功能。

xminGW 則是有人突發奇想,再進一步把 minGW 移植回去 UNIX平台,在 UNIX/Linux平台上有個 xminGW的好處是,在 UNIX 機器上可以有一個跨平台編譯器(Cross-compiler),在 UNIX平台上直接編譯產生 Windows 平台上可以執行的機器碼。Cross-compiler 在內嵌式系統開發上常用到,要內嵌的那個平台通常稱作 Target platform(標的平台),一般而言檔次較低功能上也比較陽春,並不適合在上面開發系統,大多數都是假手另一個開發程式的Source platform(可以是 Windows 或是 UNIX/Linux),透過上面的 Development toolchain 裡的 Cross-compiler 去產生出標的平台上的程式碼(通常是另一個OS平台),下載到標的平台來執行。

你的 PDA 可能上面是 Samsung 的 ARM7 或是 ARM9 處理器,他上面的應用程式其實是透過在 Linux 的 ARM Toolchain 跨平台開發出來的啦!
Likewise 你手機也是個內嵌式系統,它上面的應用程式都是透過類似的 Cross-compiler 編譯出來的啦!
http://eoffice.im.fju.edu.tw/phpbb/viewtopic.php?t=1895

DVR
Digital Video Recorder數位監控

TCC
Tiny C Compiler
http://bellard.org/tcc/

DOCBOOK
http://www.docbook.org/ 適合用來撰寫一些科學文件,但是對於數學算式的處理比tex薄弱
但是使用上比tex簡單

HIPS
主機入侵防禦系統
http://www.gsg9.tw/showthread.php?p=1350

AGPS
AGPS (Assisted GPS)可以除了用衛星定位外, 還可以其他網路設備來定位, 例如用通訊基地台
http://www.nokia.com.tw/A4805286


G-sensor
http://tw.myblog.yahoo.com/jjyeh-1976/article?mid=433&prev=434&next=417

2008年10月23日 星期四

最近在思考C/C++ & Java

無疑的C/C++是一個成功的語言,符合當時的程式設計需要,但是"最近"(約十年吧)竄起的java卻是非常的快速,我覺得C/C++在lib上的發展也不比他快
為何會這樣?有些人歸功於java的memory management的成功,其實我頗不以為然,尤其最近,記憶體對於一般應用程式來說根本不成問題;再加上OS的進步,一個不回收記憶體的程式,只要很快的結束的話,OS也可以把大部分的記憶體回收回來
後起的Ruby、Python、PHP也也感覺不大會威脅到Java,畢竟他們都還不如Java全方位,可是不能否認的,他們在web application上打下了一片天地

Java成功的法則是?我認為歸功於良好的設計跟隨著時代演化的架構。

良好的設計使得程式設計師不用在面對多種的平台,如果移植過C++到不同的平台上,你就會發現你要花上很長的時間除錯跟維護相容性,更可能只要換個compiler,你就得應付到手忙腳亂,反觀java,幾乎只要拿個jar檔,就可以正確無誤的運作(web上一些設定檔還挺麻煩的,這倒是不得不唸一下)。在意外處理上面也是相當的良好,你很容易可以找到你的錯誤,至於C++,如果不一賴debugger,你就得依賴log跟printf,真的要抓出錯誤要額外花上很多時間。當然還有相當多的地方java設計的比C++好,這就不多說了

一個良好的設計,總有一天會"腐敗",因為人們不斷的進步,java另外成功的一點就是不斷使用一些新發明的觀念、架構,不斷的改進他本身的設計,例如java2引進了swing(重要的是他背後一堆的pattern,老實說有些pattern還真不好用XD),在java 5之後,真的是大幅度的改善,java 6更是有大進步;java 2雖然用了一堆pattern,可是在做國際化(i18n),真的是"吐血身亡",swing的些GUI跟事件也是相當難處理,可是可以看到在JSR-296引進之後,開始解決這個問題,一個全新的framwork可以做更好的開發跟國際化,程式設計師不用為了國際化花費太多的心力。以這些觀念看來,java不斷的在成長,可惜C++就成長的比較緩慢

當然在效能上面C/C++真的還是遠遠把java拋在後頭(不要跟我說啥文字處理多快又多快,沒啥意義),看看那繪圖軟體、科學計算軟體、商用資料庫,哪些骨子是用java寫的,因為他們對於資源的苛求、效能的要求,這些都是讓他們把維護性擺在比較後面的理由;再者C/C++才能接觸到比較硬體方面的開發,比方驅動程式等等,畢竟這是一種極度硬體平台相關的東西。

C/C++並不會完全被java取代,但是java將是可能被推上主流開發程式的語言之一;不過還有M$挾持著OS平台優勢的C#在一旁虎視眈眈,看來java還是要努力不懈才可以

2008年10月16日 星期四

我看STL跟BOOST

這兩者無疑都是非常有力的工具,使得C++的programmer好像短時間內瞬間增加了一甲子的功力,老實說剛剛看到的時候是驚豔,後來小心對待,下一步希望可以擴充這個Lib(雖然說感覺離這目標還很遠)

驚豔:你可以想像的到,以前資料結構寫的要死要活,還得東修補西修補,才能完成你合用的東西,短時間內就可用STL完成,還可以套用多種演算法,愛用哪一種型態就是用哪一種型態,不必開發多個版本,全部人家都幫你用好了;BOOST看似一個補完計畫,以前啥atoi、printf之類的東西,好像一下子都解決的,程式的可讀性大幅提昇,以前一些平台相依性的東西,也都被加入了,好像一下子對齊了不少窗口,程式設計師不用再面對一堆不一樣的狀況

小心使用:STL跟BOOST在不瞭解他的原理的狀況下,可能會讓程式當的莫名其妙,再得到好處的時候,也犧牲了一些東西。如果你不知道在刪除了某些container裡面的內容之後,你的iterator會不能用,你大概也很難trace你的程式是如何運作,如果你不知道map其實是一個tree,你會期待他有O(1)的表現,有太多要注意的,天下沒有白吃的午餐;再者以STL來說,因為有些人很懶惰,直接引用,使得程式可讀性下降,明明就是簡單define可以解決的事情,就會看到程式裡充滿map< int,int>::iterator,這是啥鬼東西阿?是一個怎樣的map?

擴充: 人心不足蛇吞象,大家會有一些特殊的應用,在STL跟BOOST有所跟不上的地方,這時候就需要擴充他,可是說來簡單,在沒有通盤考量過STL的結構下,我們可以確定可以完全套入STL的架構中而不出錯嗎?我想很少人有這個自信,可以在STL裡面添加一個hash而套用所有的演算法跟運算子吧!?開發一個面面俱到的hash可能難度遠超乎專案的範圍

啥是BOOST?

正確的說法應該是~BOOST能做什麼?BOOST有哪些東西?
boost是一個准標準庫,相當於STL的延續和擴充,它的設計理念和STL比較接近,都是利用泛型讓復用達到最大化。不過對比STL,boost更加實用。STL集中在算法部分,而boost包含了不少工具類,可以完成比較具體的工作。

boost主要包含一下幾個大類:字符串及文本處理、容器、迭代子(Iterator)、算法、函數對象和高階編程、泛型編程、模板元編程、預處 理元編程、並發編程、數學相關、糾錯和測試、數據結構、輸入/輸出、跨語言支持、內存相關、語法分析、雜項。 有一些庫是跨類別包含的,就是既屬於這個類別又屬於那個類別。

在文本處理部分,conversion/lexcial_cast類用於「用C++」的方法實現數字類型和字符串之間的轉換。 主要是替代C標準庫中的 atoi、 itoa之類的函數。當然其中一個最大的好處就是支持泛型了。

format庫提供了對流的「printf-like」功能。printf裡使用%d、%s等等的參數做替換的方法在很多情況下還是非常方便 的,STL的iostream則缺乏這樣的功能。format為stream增加了這個功能,並且功能比原始的printf更強。

regex,這個不多說了,正則表達式庫。如果需要做字符串分析的人就會理解正則表達式有多麼有用了。

spirit,這個是做LL分析的框架,可以根據EBNF規則對文件進行分析。(不要告訴我不知道什麼是EBNF)。做編譯器的可能會用到。一般人不太用的到。

tokenizer庫。我以前經常在CSDN上看到有人問怎麼把一個字符串按逗號分割成字符串數組。也許有些人很羨慕VB的 split函數。現在,boost的tokenizer也有相同的功能了,如果我沒記錯的話,這個tokenizer還支持正則表達式,是不是很爽?

array: 提供了常量大小的數組的一個包裝,喜歡用數組但是苦惱數組定位、確定數組大小等功能的人這下開心了。

dynamic_bitset,動態分配大小的bitset,我們知道STL裡有個bitset,為位運算提供了不少方便。可惜它的大小需要在編譯期指定。現在好了,運行期動態分配大小的bitset來了。

graph。提供了圖的容器和相關算法。我還沒有在程序中用到過圖,需要用的人可以看看。

multi_array提供了對多維數組的封裝,應該還是比較有用的。

並發編程裡只有一個庫,thread,提供了一個可移植的線程庫,不過在Windows平台上我感覺用處不大。因為它是基於Posix線程的,在Windows裡對Posix的支持不是很好。

接下來的 數學和數值 類裡,包含了很多數值處理方面的類庫,數學類我也不太熟,不過這裡有幾個類還是很有用的,比如rational分數類,random隨機數類,等等。

static_assert,提供了編譯器的assert功能。

test庫,一個單元測試框架,非常不錯。

concept_check提供了泛型編程時,對泛型量的一點檢查,不是很完善,不過比沒有好。

數據類型類any,一個安全的可以包含不同對象的類。把它作為容器的元素類型,那麼這個容器就可以包含不同類型的元素。比用void *要安全。

compressed_pair,跟STL裡的pair差不多。不過對空元素做了優化。

tuple,呵呵,也許是某些人夢寐以求的東西。可以讓函數返回多個值。

跨語言支持:python,呵呵,好東東啊,可以將C++的類和函數映射給python使用。以下為幾個CSDN上的關於 boost.python的中文資料:http://dev.csdn.net/article/19/19828.shtm,http: //dev.csdn.net/article/19/19829.shtm,http://dev.csdn.net/article/19 /19830.shtm,http://dev.csdn.net/article/19/19831.shtm

pool:內存池,呵呵,不用害怕頻繁分配釋放內存導致內存碎片,也不用自己辛辛苦苦自己實現了。

smart_ptr:智能指針,這下不用擔心內存洩漏的問題了吧。不過,C++裡的智能指針都還不是十全十美的,用的時候小心點了,不要做太技巧性的操作了。

date_time,這個是平台、類庫無關的實現,如果程序需要跨平台,可以考慮用這個。

timer,提供了一個計時器,雖然不是Windows裡那種基於消息的計時器,不過據說可以用來測量語句執行時間。

uitlity裡提供了一個noncopyable類,可以實現「無法複製」的類。很多情況下,我們需要避免一個類被覆制,比如代表文件句柄的類,文件句 柄如果被兩個實例共享,操作上會有很多問題,而且語義上也說不過去。一般的避免實例複製的方法是把拷貝構造和operator=私有化,現在只要繼承一下 這個類就可以了,清晰了很多。

value_initialized:數值初始化,可以保證聲明的對象都被明確的初始化,不過這個真的實用嗎?似乎寫這個比直接寫初始化還累。呵呵,仁者見仁了。

這裡面除了regex、python和test需要編譯出庫才能用,其他的大部分都可以直接源代碼應用,比較方便。其實這些庫使用都不難。最主要的原因是有些庫的使用需要有相關的背景知識,比如元編程、STL、泛型編程等等。

來自這裡

練習boost--tuple

主要是參考IBM的網站上的一篇文章

#include <tuple.hpp>
#include <iostream>
#include <tuple_io.hpp>

using namespace std;
using namespace boost;

int main(void){
tuple<int,char,float> t(2,'a',0.9);
cout << t << endl;
return 0;
}

編譯的指令
g++ tuple_b.C -o tuple_b -I /usr/include/boost/tuple/

網頁提醒要注意兩件事情
  1. tuple裡面的元素不可超過10個
  2. tuple裡面的元素要有公開的copy constructor

對於第一點為何不可以超過10個我不大了然,不過第二個大概是當tuple再回傳或者當作參數的時候,預設大概都是bitwise的拷貝,所以在使用上就要考慮到copy constructor,我個人建議是tuple只用來存取一些value only的元素,不要用到pointer或者reference之類的,雖然威力上會打折扣,可是維護上相對應該輕鬆不少

在STL裡面使用make_pair,在boost裡面使用make_tuple,用法上是差不多了
至於取得裡面的元素,則是t.get<0>()或者boost::get<1>(t),紅色的部分表示tuple變數,藍色是第幾個參數

我個人認為,如果把pair跟tuple當作工具來使用,其實不大合乎概念,以STL來說,pair的出現是為了map之類的container存在的,為了對於container跟iterator所做出的抽象對應,而犧牲了可讀性的產品,如果把pair當作回傳一對數值的工具還不如把他變成struct;至於tuple存在的理由我現在還不大明瞭,有機會在慢慢研究