这几天在研究编译器,为了熟悉一下 FSA 在 C 语言中应该怎么实现,做了一些练习。我发现被很多代码规范所不齿的 goto 语句貌似在写状态机的时候非常有用,能够写出可读性很高的代码。如果不使用 goto,必然会引入一个状态标志,还有一大堆 switch/case,可读性反而下降了。
不妨想想看在 Ruby 中应该怎么实现?应该用些什么技巧才能避免一大堆判断语句呢?我对元编程理解不是很深入,没有丝毫头绪啊
/*
* init = initial state
* op_in = operator loaded in
* eql = equal sign loaded in
*
* | States | input | transition to
* | | |
* | init | digit | init
* | init | * + - | op_in ( with target transfer )
* | init | key_ac | init ( with resetting )
* | op_in | digit | op_in
* | op_in | * + - | op_in ( with calculation being done )
* | op_in | key_ac | init ( with resetting )
* | op_in | key_eql | init ( with calculation being done )
*/
/*
* where calculation actually being done.
* @param: [int op] loaded operator
* @param: [int lhs] left hand side operand
* @param: [int rhs] right hand side operand
* @return: result of the calculation
*/
static int do_calc(int op, int lhs, int rhs);
extern int read_key();
void calculator(void)
{
some_init_proc();
int i;
int lhs, rhs, *target;
int state, op;
lhs = rhs = 0;
target = &lhs;
display_num(0);
// State INIT
while (1) {
init:
i = read_key();
if (i < 10) {
*target *= 10;
*target += i;
display_num(*target);
} else if (i == KEY_AC) {
lhs = rhs = 0;
target = &lhs;
display_num(0);
goto init;
} else if (i == '+' || i == '-' || i == '*') {
target = &rhs;
op = i;
goto op_in;
}
}
// State OP_IN
while (1) {
op_in:
i = read_key();
if (i < 10) {
*target *= 10;
*target += i;
display_num(*target);
} else if (i == KEY_AC) {
lhs = rhs = 0;
target = &lhs;
display_num(0);
goto init;
} else if (i == '+' || i == '-' || i == '*') {
target = &rhs;
lhs = do_calc(op, lhs, rhs);
display_num(lhs);
op = i;
goto op_in;
} else if (i == KEY_EQL) {
lhs = do_calc(op, lhs, rhs);
display_num(lhs);
goto equ;
}
}
// State EQL
while (1) {
eql:
i = read_key();
if (i == KEY_AC) {
target = &lhs;
lhs = rhs = 0;
op = '+';
goto init;
}
}
}
int do_calc(int op, int lhs, int rhs)
{
switch (op) {
case '+':
return lhs + rhs;
case '-':
return lhs - rhs;
case '*':
return lhs * rhs;
default:
return lhs + rhs;
}
}