- - -
Design Patterns 的 STATE 設計模式算是比較常見或者說常須面對的問題,杜林機算是這問題的代表作,STATE 設計模式的例子,多見使用具有 OO 能力的程式語言編寫,以下這個例子採用純 C 來實作,取自 The C primer / Les Hancock, Morris Krieger。
/*
** remclr.c
**
** 這個程式可將 C 原始檔中的註解清除
** 用法:
** remclr < C 原始檔名 [> 儲存檔名]
** 不指定 儲存檔名 將顥示於螢幕
*/
#include <stdio.h>
/* 給 table[] 用來記錄要執行的動作及要改變的狀態 */
struct table_entry
{
void (*prtptr) (char C);
int next_state;
};
void noprt(char C)
{ }
void charprt(char C)
{
putchar(C);
}
void slsh(char C)
{
putchar('/');
putchar(C);
}
int main()
{
int c;
int col; /* 目前讀取原始檔字元的分類:
0 = / 字元
1 = * 字元
2 = 其他字元
*/
int row; /* 目前的狀態:
0 = 註解外
1 = 註解內
2 = 註解外碰到 / 字元的狀態
3 = 註解內碰到 * 字元的狀態
*/
static struct table_entry table[][3] =
{
{{noprt,2}, {charprt,0}, {charprt, 0}},
{{noprt,1},{noprt,3},{noprt,1}},
{{slsh,0}, {noprt,1}, {slsh,0}},
{{noprt,0}, {noprt,1}, {noprt,1}}
};
row = 0;
while((c = getchar()) != EOF)
{
if(c == '/')
col = 0;
else if(c == '*')
col = 1;
else
col = 2;
(*table[row][col].prtptr)(c);
row = table[row][col].next_state;
}
return 0;
}
因有人提出上一個程式不夠嚴謹,碰到註解位於引號內也會被刪除,所以以下修得嚴謹一點
/* ** remclr.c ** ** 這個程式可將 C 原始檔中的註解清除 ** 用法: ** remclr < C 原始檔名 [> 儲存檔名] ** 不指定 儲存檔名 將顥示於螢幕 */ #include <stdio.h>/* 給 table[] 用來記錄要執行的動作及要改變的狀態 */ struct table_entry { void (*prtptr) (char C); int next_state; }; void noprt(char C) { } void charprt(char C) { putchar(C); } void slsh(char C) { putchar('/'); putchar(C); } int main() { int c; int col; /* 目前讀取原始檔字元的分類: 0 = / 字元 1 = * 字元 2 = 其他字元 3 = 雙引號 4 = 單引號 */ int row; /* 目前的狀態: 0 = 註解外 1 = 註解內 2 = 註解外碰到 / 字元的狀態 3 = 註解內碰到 * 字元的狀態 4 = 雙引號之內 5 = 單引號之內 */ static struct table_entry table[][5] = { {{noprt,2},{charprt,0},{charprt,0},{charprt,4},{charprt,5}}, {{noprt,1},{noprt,3},{noprt,1},{noprt,1},{noprt,1}}, {{slsh,0},{noprt,1},{slsh,0},{slsh,0},{slsh,0}}, {{noprt,0},{noprt,1},{noprt,1},{noprt,1},{noprt,1}}, {{charprt,4},{charprt,4},{charprt,4},{charprt,0},{charprt,4}}, {{charprt,5},{charprt,5},{charprt,5},{charprt,5},{charprt,0}} }; row = 0; while((c = getchar()) != EOF) { if(c == '/') col = 0; else if(c == '*') col = 1; else if(c == '"') col = 3; else if(c == '\'') col = 4; else col = 2; (*table[row][col].prtptr)(c); row = table[row][col].next_state; } return 0; }
其實還要再考慮在引號內用跳脫字元使用引號的狀況,再搞下去 table 會越弄越大,以下不再用純 C 寫了,而且也比較人性化,另外發現純 c 也支援 // 註解,也一併加上
/*
** remclr.cpp
**
** 這個程式可將 C/C++ 原始檔中的註解清除
** 用法:
** remclr < C 原始檔名 [> 儲存檔名]
** 不指定 儲存檔名 將顥示於螢幕
*/
#include <iostream>
using namespace std;
class remclr
{
istream &m_Src;
ostream &m_Dest;
// 處於註解外所用的函數
void Remark_Out()
{
char c = m_Src.get();
switch(c)
{
case '/':
pmFunc_Ptr = &remclr::Remark_S;
break;
case '"':
m_Dest << c;
pmFunc_Ptr = &remclr::DoubleQuote;
break;
case '\'':
m_Dest << c;
pmFunc_Ptr = &remclr::SingleQuote;
break;
default:
m_Dest << c;
}
}
// 處於註解內所用的函數
void Remark_In()
{
char c = m_Src.get();
if( c == '*')
pmFunc_Ptr = &remclr::Remark_E;
}
// 處於註解外碰到 / 字元時所用的函數
void Remark_S()
{
char c = m_Src.get();
if(c == '*')
pmFunc_Ptr = &remclr::Remark_In;
else if(c == '/') // 碰到 // 時
pmFunc_Ptr = &remclr::TwoSlash;
else
{
m_Dest << '/';
m_Dest << c;
pmFunc_Ptr = &remclr::Remark_Out;
}
}
// 處於註解外碰到 // 時所用的函數
void TwoSlash()
{
char c = m_Src.get();
if(c == '\n')
pmFunc_Ptr = &remclr::Remark_Out;
}
// 處於註解內碰到 * 字元時所用的函數
void Remark_E()
{
char c = m_Src.get();
if( c == '/')
pmFunc_Ptr = &remclr::Remark_Out;
else if( c != '*')
pmFunc_Ptr = &remclr::Remark_In;
}
// 處於單引號時所用的函數
void SingleQuote()
{
char c = m_Src.get();
m_Dest << c;
if( c == '\'')
pmFunc_Ptr = &remclr::Remark_Out;
else if( c == '\\')
pmFunc_Ptr = &remclr::Escape_SQ;
}
// 處於雙引號時所用的函數
void DoubleQuote()
{
char c = m_Src.get();
m_Dest << c;
if( c == '"')
pmFunc_Ptr = &remclr::Remark_Out;
else if( c == '\\')
pmFunc_Ptr = &remclr::Escape_DQ;
}
// 在雙引號內有跳脫字元時
void Escape_DQ()
{
m_Dest << (char)m_Src.get();
pmFunc_Ptr = &remclr::DoubleQuote;
}
// 在單引號內有跳脫字元時
void Escape_SQ()
{
m_Dest << (char)m_Src.get();
pmFunc_Ptr = &remclr::SingleQuote;
}
void (remclr::*pmFunc_Ptr)(); // 記錄現所處狀態所要用的成員函數指標
public:
remclr(istream &Src,ostream &Dest)
:m_Src(Src),m_Dest(Dest)
{
pmFunc_Ptr = &remclr::Remark_Out;
}
~remclr()
{
}
void Start()
{
while(m_Src.eof() == false)
(this->*pmFunc_Ptr)();
}
};
int main()
{
remclr Turing(cin,cout);
Turing.Start();
return 0;
}
不過 C 版的 table 法還是比較好的,那算是一種窮盡法,或叫暴力法,可以把所有的狀況都表列出來,可以幫忙分析
沒有留言:
張貼留言