方法构建
中缀表达式转后缀表达式(逆波兰表达式)
思路一览
将传入的中缀表达式经过遍历分装入两个栈中,一个栈只存放数字和“转化后的数字”(下面代码会演示),另一个栈按运算优先级存放运算符号。遍历结束后,将符号栈(需要非空)中剩下元素依次弹出存放到数字栈中,保证所有符号都按优先级排到数字栈尾部,返回的数字栈数组即为后缀表达式(逆波兰表达式)
代码实现
import { Stack } from '@kit.ArkTS';
export class trans{
infixToPostfix(expression: string) {
//创建数字栈,符号栈,这里我额外设置了一个栈来备份元素
let result: string[] = []
let stack: string[] = []
let store :Stack<string> = new Stack() //两种方法创建都行
store.push('h') //初始化备份栈,下面转化数字要用
//划分符号优先级
const precedence = ( operator:string) => {
if (operator === "+" || operator === "-") {
return 1
} else if (operator === "×" || operator === "÷" ) {
return 2
}
else if (operator === ".")
{
return 3
}
else {
return 0
}
};
//遍历导入的字符串
for (let token of expression) {
let value = 'h'
value = store.pop() //初始化临时变量,用来存放前一位数字,也是在转换数字里用
if (/[0-9]+/.test(token) && !/[1-9]+/.test(value)&&value!==undefined) {
//元素为数字且前一位不是数字
result.push(token)
store.push(token) //数字栈、备份栈都存
}
else if(/[0-9]+/.test(token) && /[0-9]+/.test(value)&&value!==undefined)
{
//元素为数字且前一位为非0数字
let num1: number = Number(value)
let num2: number = Number(token)
let sum: number = num1*10 + num2
let new_result: string = sum.toString() //与前一位数转化为十位数
let trash = result.pop() //扔掉数字栈栈顶元素
result.push(new_result) //替换为新的十位数
store.push(new_result) //依然是两个都存
}
else if(token === "%" && value!==undefined && /[0-9]+/.test(value))
{
//元素为%且前一位为数字
let num: number = Number(value)
let num2: number = num/100
let new_result: string = num2.toString() //转为百分数
let trash = result.pop() //扔掉数字栈顶元素
result.push(new_result) //替换为新的百分数
store.push(new_result) //还是两个都存
}
else {
//剩下的就是符号了
while (stack.length > 0 && precedence(token) <= precedence(stack[stack.length - 1])) {
//根据优先级存放运算符号
const item = stack.pop()
if(item!==undefined)
result.push(item)
//这里如果直接result.push(stack.pop)会报错,所以需要加一步检验
}
stack.push(token)
store.push(token) //两个都存
}
}
//遍历结束后把剩下的运算符放入数字栈中
while (stack.length > 0) {
const item = stack.pop()
if(item!==undefined)
result.push(item)
}
return result
}
}
计算后缀表达式
思路一览
创建一个栈存放数字,遍历传入数组,将数字压入栈中,遍历到运算符就弹出栈顶的两个数字进行运算。
代码实现
import { Stack } from '@kit.ArkTS'
export class calculate{
// 逆波兰表达式方法运算
getResult(expression: string[]): number {
let num : number = 1
let stack : Stack<number> =new Stack()
for(const token of expression) //实际上是for..of循环结构,
{
if (!isNaN(Number(token))) //检查变量token是否可以被转换为一个有效的数字,如果可以转换为数字且不会产生NaN,则转化为数字并压入栈内
{
stack.push(Number(token))
}
else{
//否则,弹出栈顶的两个数字,num2和num1
const num2 = stack.pop()
const num1 = stack.pop()
switch (token){
//根据符号来进行运算
case '+':
stack.push(num1 + num2)
break
case "-":
stack.push(num1 - num2)
break
case '×':
stack.push(num1 * num2)
break
case '÷':
stack.push(num1 / num2)
break
case '.':
stack.push(num1+(num2/10))
}
}
}
// stack 可能弹出 undefined 与方法要求输出格式不符,会报错。所以在前面加一个检验方法解决报错
if(stack.pop!== undefined) //pop不加括号即可不弹出只查看
return stack.pop()
else return num
}
}
界面制作
创建单个组件
页面效果如下:
因为计算器涉及到多个按键,所以将按键的样式写入组件中,在主页面直接套用即可。
代码如下
@Component
//制作圆形按键
export struct CircleText {
private num: string = '2'
private color: Color = Color.Black
build() {
Row() {
Text(this.num)
.fontSize(35)
.fontColor(this.color)
.fontWeight(FontWeight.Regular)
}
.justifyContent(FlexAlign.Center)
.borderRadius(75)
.size({ width: 75, height: 75 }) //直接将文本框变为圆形就行
.backgroundColor(Color.White)
.shadow(ShadowStyle.OUTER_DEFAULT_SM)
}
}
@Preview
@Component
//制作长按键
export struct CircleText_long {
private num: string = '2'
private color: Color = Color.Black
build() {
Row() {
Text(this.num)
.fontSize(45)
.fontColor(this.color)
.fontWeight(FontWeight.Regular)
}
.justifyContent(FlexAlign.Center)
.borderRadius(75)
.size({ width: 75, height: 160})
.backgroundColor(Color.White)
.shadow(ShadowStyle.OUTER_DEFAULT_MD)
}
}
主页面呈现
本次主页面分为计算结果区,计算步骤区以及按键区,现在我们将之前写好的组件方法组装起来
步骤的呈现方法我使用了字符串录入的方法,即没按一个按键,该按键对应的字符会加在步骤字符串后面
.onClick(() => {
this.step = '7' //对应什么按键就是什么字符
})
创建按钮后,每个按钮要绑定相应的输入事件和计算事件,这样才能将结果同步显示在页面上。如下为代码:
@Entry
@Component
struct view {
@State
step: string = ''
//设置一个计算步骤的字符串变量
@State
tranf: string[] = new trans().infixToPostfix(this.step) //存储后缀表达式的数组
@State
result: number = new calculate().getResult(this.tranf) //存储计算结果
build() {
Column() {
Column() {
//显示答案
//判断result,进行错误报告
if (this.result === undefined) {
Text('0')
.fontColor(Color.Black)
.fontSize(65)
.fontWeight(FontWeight.Regular)
.textAlign(TextAlign.End)
.align(Alignment.Bottom)
} else {
Text(`${this.result}`)//正常显示结果
.fontColor(Color.Black)
.fontSize(65)
.fontWeight(FontWeight.Regular)
.textAlign(TextAlign.End)
.align(Alignment.Bottom)
}
}
.padding({ right: 25 })
.alignItems(HorizontalAlign.End)
.width('100%')
Column() {
Text(`${this.step}`)//答案下方显示灰色步骤
.fontColor('#989898')
.fontSize(40)
.fontWeight(FontWeight.Medium)
}
.alignItems(HorizontalAlign.End)
.justifyContent(FlexAlign.End)
.padding({ right: 30 })
.width('100%')
Row({ space: 10 }) {
//按键界面
Column({ space: 10 }) {
CircleText({ num: 'C', color: Color.Blue })
.onClick(() => {
this.step = ''
this.result = 0 //清除结果与答案
})
CircleText({ num: '7' })
.onClick(() => {
this.step += '7'
this.tranf = new trans().infixToPostfix(this.step)
this.result = new calculate().getResult(this.tranf)
//注意。每个按键都必须绑定方法事件来刷新UI
})
CircleText({ num: '4' })
.onClick(() => {
this.step += '4'
this.tranf = new trans().infixToPostfix(this.step)
this.result = new calculate().getResult(this.tranf)
})
CircleText({ num: '1' })
.onClick(() => {
this.step += '1'
this.tranf = new trans().infixToPostfix(this.step)
this.result = new calculate().getResult(this.tranf)
})
CircleText({ num: '%' })
.onClick(() => {
this.step += '%'
this.tranf = new trans().infixToPostfix(this.step)
this.result = new calculate().getResult(this.tranf)
})
}
Column({ space: 10 }) {
CircleText({ num: '÷', color: Color.Blue })
.onClick(() => {
this.step += '÷'
this.tranf = new trans().infixToPostfix(this.step)
//因为加符号不刷新,所以不用写结果方法
})
CircleText({ num: '8' })
.onClick(() => {
this.step += '8'
this.tranf = new trans().infixToPostfix(this.step)
this.result = new calculate().getResult(this.tranf)
})
CircleText({ num: '5' })
.onClick(() => {
this.step += '5'
this.tranf = new trans().infixToPostfix(this.step)
this.result = new calculate().getResult(this.tranf)
})
CircleText({ num: '2' })
.onClick(() => {
this.step += '2'
this.tranf = new trans().infixToPostfix(this.step)
this.result = new calculate().getResult(this.tranf)
})
CircleText({ num: '0' })
.onClick(() => {
this.step += '0'
this.tranf = new trans().infixToPostfix(this.step)
this.result = new calculate().getResult(this.tranf)
})
}
Column({ space: 10 }) {
CircleText({ num: '×', color: Color.Blue })
.onClick(() => {
this.step += '×'
this.tranf = new trans().infixToPostfix(this.step)
})
CircleText({ num: '9' })
.onClick(() => {
this.step += '9'
this.tranf = new trans().infixToPostfix(this.step)
this.result = new calculate().getResult(this.tranf)
})
CircleText({ num: '6' })
.onClick(() => {
this.step += '6'
this.tranf = new trans().infixToPostfix(this.step)
this.result = new calculate().getResult(this.tranf)
})
CircleText({ num: '3' })
.onClick(() => {
this.step += '3'
this.tranf = new trans().infixToPostfix(this.step)
this.result = new calculate().getResult(this.tranf)
})
CircleText({ num: '.' })
.onClick(() => {
this.step += '.'
this.tranf = new trans().infixToPostfix(this.step)
})
}
Column({ space: 10 }) {
CircleText({ num: '<=', color: Color.Blue })
.onClick(() => {
this.step = this.step.substring(0, this.step.length - 1)
this.tranf = new trans().infixToPostfix(this.step)
this.result = new calculate().getResult(this.tranf)
})
CircleText({ num: '-', color: Color.Blue })
.onClick(() => {
this.step += '-'
this.tranf = new trans().infixToPostfix(this.step)
})
CircleText({ num: '+', color: Color.Blue })
.onClick(() => {
this.step += '+'
this.tranf = new trans().infixToPostfix(this.step)
})
CircleText_long({ num: '=', color: Color.Blue })
.onClick(async () => {
this.tranf = new trans().infixToPostfix(this.step)
this.step = ''
})
}
}
.width('95%')
.height('60%')
.backgroundColor(Color.White)
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Center)
.padding({ bottom: 10 })
.borderRadius(20)
}
.justifyContent(FlexAlign.End)
.width('100%')
.height('100%')
.backgroundColor('#f1f4f9')
}
}
计算界面如下