OOC源码(三)

来源:互联网 发布:仓库管理系统源码 编辑:程序博客网 时间:2024/04/28 14:44

        《ooc》第三章针对动态链接举例,解析并计算算术表达式。《数据结构》中有关于这一块的描述,即算术表达式是某个二叉树的中序序列,示例中是将该算术表达式(中序序列)还原为一个二叉树,然后进行计算。当然该例的目的不是让我们重温数据结构,而是让我们通过该示例进一步加深对“动态链接”的理解。


        首先从二叉树着手,抽象出结点类型,具体包括分支结点(包括根结点)、叶子;分支结点对应于二元操作符(+ - * /),叶子结点对应于数值或一元操作符(负值运算符);现在对于我们需要定义的类型有了大致的了解,我们需要针对所有类型定义计算方法,用于计算以该结点为根结点的树的值。和第二章类似,我们还需要针对对象资源的分配和回收作些文章。首先声明结点类型、树(对象)管理的方法:

/* *node types */const void * Minus;const void * Value;const void * Mult;const void * Div;const void * Add;const void * Sub;/* *tree management */void * new (const void * type, ...);void process (const void * tree);void delete (void * tree);

        类型描述符定义:

struct Type {void * (* new) (va_list ap);double (* exec) (const void * tree);void (* delete) (void * tree);};

        对象管理相关方法定义:

void * new (const void * type, ...){va_list ap;void * result;assert(type && ((struct Type *) type) -> new);va_start(ap, type);result = ((struct Type *) type) -> new(ap);* (const struct Type **) result = type;va_end(ap);return result;}// 树值计算static double exec (const void * tree){assert(tree && * (struct Type **) tree&& (* (struct Type **) tree) -> exec);return (* (struct Type **) tree) -> exec(tree);}void process (const void * tree){printf("\t%g\n", exec(tree));}void delete (void * tree){assert(tree && * (struct Type **) tree&& (* (struct Type **) tree) -> delete);(* (struct Type **) tree) -> delete(tree);}

        数值结点类型定义

struct Val {const void * type;double value;};static void * mkVal (va_list ap){struct Val * node = malloc(sizeof(struct Val));assert(node);node -> value = va_arg(ap, double);return node;}static double doVal (const void * tree){return ((struct Val *) tree) -> value;}static struct Type _Value = { mkVal, doVal, free };const void * Value = & _Value;

        一元操作符及负值运算类型结点定义:

struct Un {const void * type;void * arg;};static void * mkUn (va_list ap){struct Un * node = malloc(sizeof(struct Un));assert(node);node -> arg = va_arg(ap, void *);return node;}static double doMinus (const void * tree){return - exec(((struct Un *) tree) -> arg);}static void freeUn (void * tree){delete(((struct Un *) tree) -> arg);free(tree);}static struct Type _Minus = { mkUn, doMinus, freeUn };const void * Minus = & _Minus;

        二元操作符类型定义:

struct Bin {const void * type;void * left, * right;};static void * mkBin (va_list ap){struct Bin * node = malloc(sizeof(struct Bin));assert(node);node -> left = va_arg(ap, void *);node -> right = va_arg(ap, void *);return node;}static double doAdd (const void * tree){return exec(((struct Bin *) tree) -> left) +exec(((struct Bin *) tree) -> right);}static double doSub (const void * tree){return exec(((struct Bin *) tree) -> left) -exec(((struct Bin *) tree) -> right);}static double doMult (const void * tree){return exec(((struct Bin *) tree) -> left) *exec(((struct Bin *) tree) -> right);}static double doDiv (const void * tree){double left = exec(((struct Bin *) tree) -> left);double right = exec(((struct Bin *) tree) -> right);if (right == 0.0)error("division by zero");return left / right;}static void freeBin (void * tree){delete(((struct Bin *) tree) -> left);delete(((struct Bin *) tree) -> right);free(tree);}static struct Type _Add = { mkBin, doAdd, freeBin };static struct Type _Sub = { mkBin, doSub, freeBin };static struct Type _Mult = { mkBin, doMult, freeBin };static struct Type _Div = { mkBin, doDiv, freeBin };const void * Add = & _Add;const void * Sub = & _Sub;const void * Mult = & _Mult;const void * Div = & _Div;

        这样,我们就将所需结点类型描述完毕了。下面主要叙述表达式解析,这部分在《数据结构》中已经晒了很多。

        我们在针对某个表达式解析时,需要判断当前的symbols是空格、数值还是字符(运算符、括号等);处理时首先忽略掉空格,这就需要标记当前的东东是数值还是字符,我们用枚举值NUMBER表示数值:

enum tokens {/* must not clash with operators */NUMBER = 'n'/* literal constant */};

        使用token变量标记是字符还是数值,如果是数值便将数值存入number变量中:

static enum tokens token;/* current input symbol */static double number;/* if NUMBER: numerical value */

        下面给出字符和数值的提取方法:

static enum tokens scan (const char * buf)/* return token = next input symbol */{static const char * bp;if (buf)bp = buf;/* new input line */while (isspace(* bp & 0xff))++ bp;if (isdigit(* bp & 0xff) || * bp == '.'){errno = 0;token = NUMBER, number = strtod(bp, (char **) & bp);if (errno == ERANGE)error("bad value: %s", strerror(errno));}else{token = * bp ? * bp ++ : 0;}return token;}

        下面三个方法factor、product、sum是用来生成树的,内部使用递归,不方便多讲;factor主要负责创建一元操作符结点(负值)和数值(对象),就是终端节点(树叶);product创建二元操作符结点(* /),即子树;sum负责创建二元操作符结点(+ -),即树的主干。

/* *factor : + factor * - factor * NUMBER * ( sum ) */static void * sum (void);static void * factor (void){void * result;switch (token) {case '+':scan(0);return factor();case '-':scan(0);return new(Minus, factor());default:error("bad factor: '%c' 0x%x", token, token);case NUMBER:result = new(Value, number);break;case '(':scan(0);result = sum();if (token != ')')error("expecting )");}scan(0);return result;}/* *product : factor { *|/ factor }... */static void * product (void){void * result = factor();const void * type;for (;;){switch (token) {case '*':type = Mult;break;case '/':type = Div;break;default:return result;}scan(0);result = new(type, result, factor());}}/* *sum : product { +|- product }... */static void * sum (void){void * result = product();const void * type;for (;;){switch (token) {case '+':type = Add;break;case '-':type = Sub;break;default:return result;}scan(0);result = new(type, result, product());}}

        此外,还提供了异常处理程序,增强程序健壮性

static jmp_buf onError;void error (const char * fmt, ...){va_list ap;va_start(ap, fmt);vfprintf(stderr, fmt, ap), putc('\n', stderr);va_end(ap);longjmp(onError, 1);}


       主函数:
int main (void){volatile int errors = 0;char buf [BUFSIZ];if (setjmp(onError))// 异常恢复断点++ errors;while (fgets(buf, sizeof buf, stdin))if (scan(buf)){void * e = sum();// 创建树if (token)error("trash after sum");process(e);// 计算树值delete(e);// 释放树}return errors > 0;}





0 0
原创粉丝点击