昨天见有人想用RGSS实现一个计算器,于是我想看看逆波兰表达式,找到了一个好博客,于是昨天晚上实现了算法,今天优化了数据输入。
这次实验真正了解了ruby编程的方便,可以直接关心核心算法部分。这次对正则表达式的用法也加深了。
参考的资料
前缀、中缀、后缀表达式 讲的很清晰
正则表达式分组、断言详解 解决了使用断言的问题
# -*- coding:utf-8 -*-
=begin
中缀表达式转后缀表达式
测试数据:
-12+33*(-32+9)/-3 #负数
12+(3-7*2+15)*5-(2+(5-8)*2+3) #嵌套括号
12.6/2-1.1*5+2 #小数
解决的问题:
利用正则表达式“零宽度正后发断言”解决负数问题(“先行”不可以)
为了解决开头为负数的问题,字符串前加入空格
制作过程:
1.先实现算法
2.利用正则表达式优化输入
=end
@debug=1 # 0 1 2 看程序
@operator=Array[2]
@operator[0]=%w[+ - * /]
@operator[1]=%w[( )]
#不考虑负数
#@digit_reg=/^ \d+ (\.\d+)? $/x #x忽略空格
#@digit_reg=/(?:)?\d+(?:\.\d+)?/ #加上?: scan才正常
@digit_reg=/(?:(?<=\D)-)?\d+(?:\.\d+)?/ #解决负数问题
@ope_reg=/ \+ | \- | \* | \/ | \( | \) /x # - 也行
@ope_method={}
@ope_method["+"]=lambda{|x,y| x+y}
@ope_method["-"]=lambda{|x,y| x-y}
@ope_method["*"]=lambda{|x,y| x*y}
@ope_method["/"]=lambda{|x,y| x/y}
def infix_to_suffix arr
s1,s2=[],[] #stack
arr.each do |x|
if x =~ @digit_reg #数字处理
s2.push format("%.2f",x).to_f
else #非数字处理
if @operator[0].include? x # 运算符处理
if s1.size==0 or
s1[-1]==@operator[1][0] or
(@operator[0].index x) > (@operator[0].index s1[-1]) then
s1.push x
else
s2.push s1.pop
redo #可用于块
end
elsif @operator[1].include? x #括号处理
if x==@operator[1][0]
s1.push x
else
s2.push s1.pop until s1.size==0 || s1[-1]==@operator[1][0]
if s1.size==0
STDERR<<"error: bracket is not match\n"
exit
else
s1.pop
end
end
else
STDERR<<"error: input symbol\n"
exit
end
end
end
while s1.size !=0
s2.push s1.pop
end
s2
end
def comput_suffix arr
s_num=[] #also a stack
arr.each do |x|
if x.class == Float
s_num.push x
elsif s_num.size>=2
a,b=s_num.pop,s_num.pop
r=@ope_method[x].call b,a
puts "#{b} #{x} #{a} = #{r}" if @debug>=1
s_num.push r
else
break
end
end
if s_num.size!=1
STDERR<<"error: the equ is not true\n"
exit
end
format("%.2f", s_num[0] ).to_f
end
def split_num_ope str
#为了实现方便的输入
outarr=[]
#def不能传入local变量 *****
define_method :handle_ope_str do |str|
while(str=~@ope_reg)
outarr.push $&
str=$'
end
end
while(str=~@digit_reg)
pre,now,nxt=$`,$&,$' #匹配 前中后,保存下来否则会改变
handle_ope_str pre.strip unless pre.nil? or pre.strip==""
outarr.push now
str=nxt
puts pre+" / "+now+" / "+nxt if @debug>=2
end
handle_ope_str str.strip unless str.nil? or str.strip==""
p outarr if @debug>=1
outarr
end
instr=" "+gets #输入
#p instr.match @digit_reg 只匹配一个,返回metadata
#instr.scan(@digit_reg).each do |m| puts m ; end
#inarr=instr.split /\s+/ #/ #最初使用的方法
inarr=split_num_ope instr
suf=infix_to_suffix inarr
p comput_suffix suf