代码改变世界

Structures

2017-02-19 15:22  星星之火✨🔥  阅读(537)  评论(0)    收藏  举报

1、在TCPL Reading Notes 中第79 条中,getword 函数不能正确处理下划线、字符串常量、注释及预处理器控制指令。请编写一个更完善的getword 函数。

#include <stdio.h>
#include <ctype.h>

// getword: get next word or character from input
int getword(char *word, int lim)
{
	int c, d, comment(void), getch(void);
	void ungetch(int);
	char *w = word;
	
	while(isspace(c = getch()))
		;
	if(c != EOF)
		*w++ = c;
	if(isalpha(c) || c == '_' || c == '#')
	{
		for(; --lim > 0; w++)
			if(!isalnum(*w = getch()) && *w != '_')
			{
				ungetch(*w);
				break;
			}
	}
	else if(c == '\'' || c == '"')
	{
		for(; --lim > 0; w++)
			if((*w = getch()) == '\\')
				*++w = getch();
			else if(*w == c)
			{
				w++;
				break;
			}
			else if(*w == EOF)
				break;
	}
	else if(c == '/')
		if((d = getch()) == '*')
			c = comment();
		else
			ungetch(d);
	
	*w = '\0';
	return c;
} 

// comment: skip over comments and return a character
int comment(void)
{
	int c;
	
	while((c = getch()) != EOF)
		if(c == '*')
			if((c = getch()) == '/')
				break;
			else
				ungetch(c);
				
	return c;
}

为了处理下划线和预编译器指令,我们把原来的if(!isalpha(c)) 语句修改为if(isalpha(c) || c == '_' || c == '#'),跟在_ 和# 后面的字母和数字字符将被看作是单词的一部分。

字符串常量可能出现在单引号或双引号中,一旦检测到左引号,我们就把该左引号到与之邻近的右引号或EOF 标记之间的全部字符收集起来,并把它们当作一个字符串常量来对待。

如果遇到注释,我们就跳过其内容并返回它的结尾斜线字符。

2、编写一个程序,根据单词的出现频率按降序打印输入的各个不同单词,并在每个单词的前面标上它的出现次数。

#include <stdio.h>
#include <ctype.h>

#define MAXWORD 100
#define NDISTINCT 1000

struct tnode {
	char *word; // the tree node;
	int count; // points to the text
	struct tnode *left; // left child
	struct tnode *right; // right child
};

struct tnode *addtree(struct tnode *, char *);
int getword(char *, int);
void sortlist(void);
void treestore(struct tnode *);

struct tnode *list[NDISTINCT]; // pointers to tree nodes
int ntn = 0; // number of tree nodes

// print distinct words sorted in decreasing order of freq.
int main(void)
{
	struct tnode *root;
	char word[MAXWORD];
	int i;
	
	root = NULL;
	while(getword(word, MAXWORD) != EOF)
		if(isalpha(word[0]))
			root = addtree(root, word);
	treestore(root);
	sortlist();
	for(i = 0; i < ntn; i++)
		printf("%2d:%20s\n", list[i]->count, list[i]->word);
	
	return 0;
}

struct tnode *talloc(void); // make a tnode
char *strdup(char *); // make a duplicate of s
// addtree: add a node with w, at or below p
struct tnode *addtree(struct tnode *p, char *w)
{
	int cond;
	
	if(p == NULL) // a new word has arrived
	{
		p = talloc(); // make a new node
		p->word = strdup(w);
		p->count = 1;
		p->left = p->right = NULL;
	}
	else if((cond = strcmp(w, p->word)) == 0)
		p->count++; // repeated word
	else if(cond < 0) // less than into left subtree
		p->left = addtree(p->left, w);
	else // greater than into right subtree
		p->right = addtree(p->right, w);
		
	return p;
}

// treestore: store in list[] pointers to tree nodes
void treestore(struct tnode *p)
{
	if(p != NULL)
	{
		treestore(p->left);
		if(ntn < NDISTINCT)
			list[ntn++] = p;
		treestore(p->right);
	}
}

// sortlist: sort list of pointers to tree nodes
void sortlist()
{
	int gap, i, j;
	struct tnode *temp;
	for(gap = ntn/2; gap > 0; gap /= 2)
		for(i = gap; i < ntn; i++)
			for(j = i-gap; j >= 0; j -= gap)
			{
				if((list[j]->count) >= (list[j+gap]->count))
					break;
				temp = list[j];
				list[j] = list[j+gap];
				list[j+gap] = temp;
			}
}


// getword: get next word or character from input
int getword(char *word, int lim)
{
	int c, getch(void);
	void ungetch(int);
	char *w = word;
	
	while(isspace(c = getch()))
		;
	if(c != EOF)
		*w++ = c;
	if(!isalpha(c))
	{
		*w = '\0';
		return c;
	}	
	for(; --lim > 0; w++)
		if(!isalnum(*w = getch()))
		{
			ungetch(*w);
			break;
		}
	*w = '\0';
	return word[0];
}

#include <stdlib.h>
// talloc: make a tnode
struct tnode *talloc(void)
{
	return (struct tnode *)malloc(sizeof(struct tnode));
}

// strdup: make a duplicate of s
char *strdup(char *s)
{
	char *p;
	
	p = (char *)malloc(strlen(s) + 1); // +1 for '\0'
	if(p != NULL)
		strcpy(p, s);
	
	return p;
}

常数NDISTINCT 对不同单词的最大数目进行了限制。list 是一个指针数组,其中的每个指针都指向一个tnode 类型的结构。变量ntn在treestore 函数运行结束后保存了树节点的个数。程序读入每个单词并把它放到树中。函数treestore 把那些指向tnode 结构的指针保存到数组list 中。函数sortlist 是shellsort 函数的改进版本,它按单词出现次数由高到低的顺序对数组list 进行排序。

3、针对TCPL Reading Notes 中第82 条散列表查找编写一个函数,它将从由lookup 和install 维护的表中删除一个变量名及其定义。

unsigned hash(char *);

// undef: remove a name and definition from the table
void undef(char *s)
{
	int h;
	struct nlist *prev, *np;
	
	prev = NULL;
	h = hash(s); // hash value of string s
	for(np = hashtab[h]; np != NULL; np = np->next)
	{
		if(strcmp(s, np->name) == 0)
			break;
		prev = np; // remember previous entry
	}
	if(np != NULL) // found name
	{
		if(prev == NULL) // first in the hash list ?
			hashtable[h] = np->next;
		else // elsewhere in the hash list
			prev->next = np->next;
		
		free((void *)np->name);
		free((void *)np->defn);
		free((void *)np); // free allocated structure
	}
}

函数undef 将在表中查找字符串s。当undef 找到字符串时,它将跳出for 循环,如果字符串s 不在表中,循环将在指针np 变成NULL 时终止。

指针数组hashtab 中的各个元素分别指向一个链表的开头。如果指针不为NULL,就说明其所指向的那个表中存在一组符合清除要求的变量名和定义;此时,指针np 指向将被清除的那个数据项,而指针prev 则指向出现在np 位置之前的那个数据项。如果prev 是NULL,就说明np 是以hashtab[h] 开头的那个链表的第一个元素。

在清除了np 所指的数据项之后,我们还有通过free 函数把该数据项的名字、定义及其存储结构都释放。