1. 程式人生 > >表示式求值之遞迴下降

表示式求值之遞迴下降

本文旨在討論使用遞迴下降技術計算只含實數、+、-、*、/和()的數學表示式的值的問題,我只是一名大二的學生,為完成資料結構作業而研究該技術,所以瞭解的很膚淺,歡迎評論。

我們可以使用一種遞迴的方式來定義數學表示式,首先我寫出對一個數學表示式Expression的的遞迴定義形式:

Expression = Term + Expression | Term - Expression | Term
Term = Factor * Term | Factor / Term | Factor
Factor = (Expression) | 實數

我解釋下,每行代表一種表示式的生成規則,中間的豎線“|”意思是或。如:Factor = (Expression) | 實數,意思是一個Factor可以是被括號括起來的一個表示式,或者一個實數。這種定義形式利用的遞迴的思想。

下面,注意到每個表示式中都會有遞迴,但注意到每種表示式的生成規則中,運算子都是同級的,所以遞迴可以用迭代消除:

Expression = Term +或- Term +或- Term …… …… …… +或- Term
Term = Factor *或/ Factor *或/ Factor …… …… …… *或/ Factor
Factor = (Expression) | 實數

下面的我直接在程式碼的註釋中如何用程式碼實現。

#include <stdio.h>
#include <stdlib.h>

const int MAXN = 10000;

char inputString[MAXN+1];	//輸入的表示式
int stringIndex = 0;	//全域性變數,代表掃描到的位置

//函式宣告,每個函式名對應用於計算遞迴定義中的一項
double Expression();
double Term();
double Factor();

double Expression()
{
	double numberA,numberB;
	numberA = Term();

	//迭代求Term +或- Term +或- Term …… …… …… +或- Term
	while (inputString[stringIndex] == '+' || inputString[stringIndex] == '-')
	{
		char op = inputString[stringIndex++];	//運算子

		numberB = Term();	//看吧、呼叫Term了
		switch (op)
		{
		case '+':
			numberA += numberB;
			break;
		case '-':
			numberA -= numberB;
			break;
		default:
			break;
		}
	}

	if (inputString[stringIndex] == ')')	//對應計算Factor = (Expression)時被呼叫,吃掉)
		stringIndex++;

	return numberA;
}

double Term()
{
	double numberA,numberB;
	numberA = Factor();

	while (inputString[stringIndex] == '*' || inputString[stringIndex] == '/')
	{
		char op = inputString[stringIndex++];

		numberB = Factor();		//呼叫Factor
		switch (op)
		{
		case '*':
			numberA *= numberB;
			break;
		case '/':
			numberA /= numberB;
			break;
		default:
			break;
		}
	}

	return numberA;
}

double Factor()
{
	if(inputString[stringIndex] == '(')		//遇到(,由Factor = (Expression),吃掉左括號並計算括號中的Expression
	{
		stringIndex++;
		return Expression();
	}
	else	//否則直接掃描實數,關於strtod的用法請自己查證百度百科
	{
		char *endptr;
		double numberA = strtod(inputString+stringIndex,&endptr);
		stringIndex = endptr-inputString;	//這一步用於在strtod函式讀取完實數後,將stringIndex調至實數結尾的下一個位置
		return numberA;
	}
}

int main()
{
	for(;;)
	{
		scanf("%s",inputString);
		if(isalpha(*inputString)) break;
		stringIndex = 0;
		printf("=%lf\n",Expression());
	}

	return 0;
}

這個方法比課本上講的棧更簡潔,程式結構化更強,而且很好理解啊。

歡迎大家評論糾正以及補充。