HarmonyOS----简易计算器程序制作随笔

方法构建

中缀表达式转后缀表达式(逆波兰表达式)

思路一览

将传入的中缀表达式经过遍历分装入两个栈中,一个栈只存放数字和“转化后的数字”(下面代码会演示),另一个栈按运算优先级存放运算符号。遍历结束后,将符号栈(需要非空)中剩下元素依次弹出存放到数字栈中,保证所有符号都按优先级排到数字栈尾部,返回的数字栈数组即为后缀表达式(逆波兰表达式)

代码实现
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')

  }
}

计算界面如下

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值