C語言和抽象思維(二)
上一次我們說到C語言結合抽象思維完成一個非所見即所得的編輯器, 并且我們已經定義了這個編輯器應有的行為, 基本上抽象也已經完成。這一節講的更多是實現上的事情。光有設計思路是不夠的, 到最后我們得作出一點什么東西才行。
數組實現
字符串緩沖區有什么特點呢?首先我們需要記錄光標位置, 其次要能對字符進行增刪, 很自然的我們可以想到用數組來進行表示。數組表示可以輕易的記錄當前光標的位置, 只需要記錄下標值就可以。并且緩沖區中的字符十一個有序的同類序列, 這和數組的表示相吻合。但由于C語言中為數組申請空間時必須知道數組大小, 所以我們需要一個值記錄現在已經使用了多少個字符。于是我們把結構體bufferCDT
定義如下:
#define MaxBuffer 100
struct bufferCDT {
char text[MaxBuffer];
int length; // 目前已經使用的長度
int cursor; // 光標的位置
};</pre></code>
接下來就只需要把對字符串進行操作的幾個函數實現就可以了, 但因為我們需要把函數和數據分離, 也就是說, 可以同時并發調用這個函數, 但各個不同調用函數的人之間數據不會相互影響。所以我們的函數應該定義為這樣子:void InsertCharacter(bufferADT buffer, char ch)
每次都把bufferADT的實體傳進去, 那么函數進行操作的時候就會有單獨的一塊空間, 多次調用相同函數不會相互影響。
我想, 在當前光標下刪除字符、插入字符你一定可以自己動手完成的!什么?你不確定?那好, 我給你一個參考思路:對于刪除字符, 首先要檢查當前光標位置是否在有效范圍之內,如果是, 那么直接把光標之后的所有字符向前移動一位,然后buffer->cursor--;
; 對于插入字符, 需要先檢查目前有效范圍是不是超過最大范圍, 如果沒有,那么把光標后的所有字符向后移動一位,然后把字符插入進字符串, 最后buffer->length++; buffer->cursor++;
。
光標移動?那更簡單了, 我相信你可以的!
DisplayBuffer
的實現:
void DisplayBuffer(bufferADT buffer)
{
int i;
for(i = 0; i < buffer->length; i++) {
printf(" %c", buffer->text[i]);
}
printf(" \n");
for(i = 0; i < buffer->cursor; i++) {
printf(" ");
}
printf("^\n");
}</pre></code>
棧實現
棧?編輯器?反正我一開始是沒想到可以用棧表示。但思路其實很簡單:分別用兩個棧, 一個表示光標之前的字符, 一個表示光標之后的字符。原來是這樣!
這讓我想到火影忍者里的一個小段子:鳴人在修煉風遁螺旋手里劍的時候非常努力,但由于需要大量查克拉,并且很多分身在同時修煉,九尾的查克拉很容易溢出。后來下雨了,鳴人累得趴下了,向卡卡西抱怨,風遁螺旋手里劍就像走路的時候,一邊要看左邊,同時還要看右邊,這怎么做的到啊!卡卡西說, 哦, 這很簡單啊, 于是就使用了一個影分身,一個負責看左邊,一個負責看右邊。這里也是一樣的, 一開始我在想,用棧怎么表示緩沖區?同時記錄一個索引位置嗎?這樣子很不方便啊!翻到這一頁的時候才發現,可以用兩個棧,一個表示光標前,一個表示光標后。。。
</blockquote>
數據結構該怎么定義呢? 如上面所說:用兩個棧!
struct bufferCDT {
stackADT before;
stackADT after;
};
用棧其實很方便, 完成移動光標的操作只需要一個Push, 一個Pop就可以了。完成刪除只需要Pop并丟棄該字符、插入只需要Push就可以。
別看我,我可沒有代碼, 你一定可以自己寫出來的!
總結
我們看了兩種實現, 該總結一下了, 不知道大家有沒有發現, 我們用了兩種表現方式, 但是代碼的接口卻完全沒動!這就是抽象的好處。 抽象可以讓邏輯和實現分開, 只要實現提供能完成功能的函數, 實現隨便改, 而邏輯動都不要動!感覺到了嗎?為了驗證我們的總結, 我們再說一種實現 ---- 鏈表實現。
鏈表實現
鏈表有什么好處呢?首先, 只要內存扛得住, 編輯器緩沖區可以無限長!其次,相比棧和數組表示, 把光標移動到緩沖區的首部和尾部時消耗特別小, 再者, 打字出錯是經常發生地事情, 如果在緩沖區內插入完數據后發現,在最前面漏了一個字符! 如果我們用的是字符表示的話, 電腦會說“jerk!你想累死我是吧!”, 因為數組需要把一大堆字符全部往后移動, 然后才能插入! 棧表示? 電腦也會罵你的! 棧也需要一大把的Pop和Push!
鏈表實現實際上也很方便!但是有一個小坑, 如果你認真自己思考的話, 很快你就會發現的, 當然,你發現以后要解決那就更簡單了。鏈表表示由于需要畫大量的圖,Linux下我也一直沒找到一個順手的畫圖工具, 就先不寫了!容我偷偷懶
鏈表表示你就當作是習題吧。習題二:用雙向鏈表表示一下!
</blockquote>
資料
我把代碼打了個包, 你可以下載:
來自:http://gansteed.github.io/2014/12/18/abstractions-with-c-02/