中缀表达式转换为前缀表达式(lisp实现)

使用weight、opcode和infix_to_prefix三个函数实现中缀表达式到前缀表达式的转换。

算符优先级函数weight

首先定义函数weight,它返回一个算术运算符(可简称为算符)的优先级(优先权)。

;确定算符的优先权
(defun weight (operator) 
	(cond 
		((equal operator 'dummy) -1) ;运算符号表的开始标记
		((equal operator '=) 0)  ;等于号
		((equal operator '+) 1)	;加号
		((equal operator '-) 1)	;减号
		((equal operator '*) 2)	;乘号
		((equal operator '/) 2);除号
		((equal operator '\\) 2);求余运算符
		((equal operator '^) 3) ;指数运算符
		(t (print (list operator 'not 'an 'operator)) 'nop)
	)
)

在有些LISP的实现中,反斜杠“/”表示对下一个字符做特殊处理,如阻止把空格解释为分隔符,所以在上述函数中用双反斜杠“\\”等价地表示单斜杠“\”。

上述函数中的伪运算符dummy用来标记算符表的开端。

算符到操作码的转换函数opcode

定义算符到操作码的转换函数opcode。

;计算与operator对应的lisp函数名.
(defun opcode (operator) 
	(cond
		((equal operator 'dummy)
			(print 'hit-dummy) 
			'dummy
		)
		((equal operator '=) 'setq)
		((equal operator '+) 'plus)
		((equal operator '-) 'difference)
		((equal operator '*) 'times)
		((equal operator '/) 'quotient)
		((equal operator '\\ ) 'remainder)
		((equal operator '^) 'expt)
		(t (print (list operator 'not 'an 'operator)) 'nop)
	)
)

中缀表达式转换为前缀表达式的函数infix_to_prefix

从一种形式到另一种形式的转换方法有多种,这里采用自左向右的线性扫描法。其中,尚未用来产生输出的操作数和运算符都保留在表内。infix_to_prefix函数使用operands和operators两张表分别保存操作数和运算符(操作符)。

;将算术表达式由中缀形式变为前缀形式
(defun infix_to_prefix (ae)
	(prog (operands operators) ;操作数表和运算符表
		(cond((atom ae) (return ae))) ;特殊情况,只有一个操作数
		(setq operators (list 'dummy)) ;虚拟终结符
		stuff ;寻找操作数
			(cond
				((null ae) 扫描操作数,以运算符结尾
					(return 'unexpected-end)
				)
			) 
			;递归
			(setq 
				operands ;操作数入栈
					(cons 
						(cond 
							((atom (car ae)) (car ae)) 
							(t (infix_to_prefix (car ae)))
						)
						operands
					)	;设置operands
				ae (cdr ae) ;删除操作数,设置ae
			)
		scan ;扫描运算符
			(cond
				((and(null ae) (equal (car operators) 'dummy)) ;ae及运算符表为空
					(return (car operands))	;回送结果,operands即为所需的前缀表达式
				)
			)
			(cond
				;ae为空或运算符表的第一个算符的优先级高于ae的第一个算符的优先级
				((or (null ae)	
					(not (> (weight (car ae)) (weight (car operators)))))  ;嵌套次序.
					(setq 
						operands ;设置operands
							(cons (list (opcode (car operators)) (cadr operands) (car operands))
								(cddr operands)) ;弹出两个操作数
						operators (cdr operators);弹出一个运算符
					)
					(go scan) ;继续寻找运算符。
				)
				;否则
				(t 
					(setq 
						operators (cons (car ae) operators) ; 运算符入栈
						ae (cdr ae) ;从ae中删除算符
					)
					(go stuff)
				)
			)
	)
)

三个函数放在一起

为了便于使用和复制,将三个函数放在一起

;确定算符的优先权
(defun weight (operator) 
	(cond 
		((equal operator 'dummy) -1) ;运算符号表的开始标记
		((equal operator '=) 0)  ;等于号
		((equal operator '+) 1)	;加号
		((equal operator '-) 1)	;减号
		((equal operator '*) 2)	;乘号
		((equal operator '/) 2);除号
		((equal operator '\\) 2);求余运算符
		((equal operator '^) 3) ;指数运算符
		(t (print (list operator 'not 'an 'operator)) 'nop)
	)
)

;计算与operator对应的lisp函数名.
(defun opcode (operator) 
	(cond
		((equal operator 'dummy)
			(print 'hit-dummy) 
			'dummy
		)
		((equal operator '=) 'setq)
		((equal operator '+) 'plus)
		((equal operator '-) 'difference)
		((equal operator '*) 'times)
		((equal operator '/) 'quotient)
		((equal operator '\\ ) 'remainder)
		((equal operator '^) 'expt)
		(t (print (list operator 'not 'an 'operator)) 'nop)
	)
)

;将算术表达式由中缀形式变为前缀形式
(defun infix_to_prefix (ae)
	(prog (operands operators) ;操作数表和运算符表
		(cond((atom ae) (return ae))) ;特殊情况,只有一个操作数
		(setq operators (list 'dummy)) ;虚拟终结符
		stuff ;寻找操作数
			(cond
				((null ae) 扫描操作数,以运算符结尾
					(return 'unexpected-end)
				)
			) 
			;递归
			(setq 
				operands ;操作数入栈
					(cons 
						(cond 
							((atom (car ae)) (car ae)) 
							(t (infix_to_prefix (car ae)))
						)
						operands
					)	;设置operands
				ae (cdr ae) ;删除操作数,设置ae
			)
		scan ;扫描运算符
			(cond
				((and(null ae) (equal (car operators) 'dummy)) ;ae及运算符表为空
					(return (car operands))	;回送结果,operands即为所需的前缀表达式
				)
			)
			(cond
				;ae为空或运算符表的第一个算符的优先级高于ae的第一个算符的优先级
				((or (null ae)	
					(not (> (weight (car ae)) (weight (car operators)))))  ;嵌套次序.
					(setq 
						operands ;设置operands
							(cons (list (opcode (car operators)) (cadr operands) (car operands))
								(cddr operands)) ;弹出两个操作数
						operators (cdr operators);弹出一个运算符
					)
					(go scan) ;继续寻找运算符。
				)
				;否则
				(t 
					(setq 
						operators (cons (car ae) operators) ; 运算符入栈
						ae (cdr ae) ;从ae中删除算符
					)
					(go stuff)
				)
			)
	)
)

在portacle中添加定义,如下图所示:

程序的运行

依次输入命令

 (infix_to_prefix '(A + B * C) )

 (infix_to_prefix '(total = principal * ( 1.0 + interest ) ^ years ) )

运行结果如下:

 

 

要将中缀表达式转换前缀表达式,可以使用以下步骤: 1. 反转中缀表达式:将中缀表达式从右到左扫描,并将操作数和运算符的顺序反转。 2. 将左右括号互位置:将左括号变为右括号,将右括号变为左括号。 3. 使用栈进行转换:创建一个空栈,以及一个空的前缀表达式字符串。 4. 从左到右遍历反转后的中缀表达式: - 如果遇到操作数,则直接添加到前缀表达式字符串中。 - 如果遇到右括号,则将其推入栈中。 - 如果遇到左括号,则将栈中的运算符逐个弹出并添加到前缀表达式字符串,直到遇到右括号为止。最后,弹出栈顶的右括号。 - 如果遇到运算符(+、-、*、/等),则将栈中的运算符逐个弹出并添加到前缀表达式字符串,直到遇到比当前运算符优先级低或者遇到左括号为止。然后将当前运算符推入栈中。 5. 遍历完所有元素后,将栈中剩余的运算符逐个弹出并添加到前缀表达式字符串。 最后,得到的前缀表达式字符串就是转换后的前缀表达式。 举个例子,将中缀表达式 "3 + 4 * 2 / ( 1 - 5 )^2^3" 转换前缀表达式: 1. 反转中缀表达式:"3^2^3 / 1 - 5 ) ( 2 * 4 + 3" 2. 互括号位置:"3^2^3 / 1 - 5 ( ) 2 * 4 + 3" 3. 使用栈进行转换: - 遍历反转后的中缀表达式: - 遇到操作数 "3",添加到前缀表达式字符串。 - 遇到运算符 "^",将其推入栈中。 - 遇到操作数 "2",添加到前缀表达式字符串。 - 遇到运算符 "^",将其推入栈中。 - 遇到操作数 "3",添加到前缀表达式字符串。 - 遇到运算符 "/",将栈中的运算符 "+" 弹出到前缀表达式字符串。 - 遇到操作数 "1",添加到前缀表达式字符串。 - 遇到运算符 "-",将栈中的运算符 "/" 弹出到前缀表达式字符串。 - 遇到操作数 "5",添加到前缀表达式字符串。 - 遇到右括号,将栈中的运算符 "*" 弹出到前缀表达式字符串。 - 遇到右括号,将栈中的运算符 "(" 弹出。 - 遇到左括号,将 "*" 推入栈中。 - 遇到操作数 "2",添加到前缀表达式字符串。 - 遇到运算符 "*",将栈中的运算符 "+" 弹出到前缀表达式字符串。 - 遇到操作数 "4",添加到前缀表达式字符串。 - 遇到运算符 "+",将栈中的运算符 "*" 弹出到前缀表达式字符串。 - 遇到操作数 "3",添加到前缀表达式字符串。 - 栈为空,遍历完毕。 4. 得到前缀表达式:"- / ^ 3 ^ 2 3 * 2 + 4 3 1 5" 注意:以上步骤是一种常用的转换方法,但对于复杂的中缀表达式,可能需要更复杂的算法来处理运算符优先级和结合性等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alexabc3000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值