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
- OOC源码(三)
- OOC源码(二)
- 关于高阻态和OOC(out of context)综合方式
- OOC-GCC 特性介绍
- 源码阅读(三)
- 源码(三)
- 源码有毒:Jfinal源码解析(三)
- JUnit源码分析(三)
- Log4net源码分析(三)
- Azureus源码剖析(三)
- Log4net源码分析(三)
- struts2源码浅析(三)
- Logcat源码分析(三)
- SEDA源码解读(三)
- chromium源码剖析(三)
- Log4net源码分析(三)
- chromium源码剖析(三)
- pomelo源码分析(三)
- 几个IOS比较炫的效果
- 两年.net码农总结
- Labview中串口通讯的研究
- 捕捉全屏图像
- 尼康轻便型相机COOLPIX S系列新机发布
- OOC源码(三)
- Error -27727: Step download timeout (120 seconds) has expired when downloading
- Java日志系统框架的设计与实现
- Eclipse中Jquery等js文件报错的解决办法
- Scriptlet笔记1
- Vbscript在DataGridView中折半查找某一行
- Oracle 创建用户 修改用户密码 授权命令
- soc camera 子系统简介
- 亚马逊凭什么成为网络巨头