【iOS】计算器的仿写

news2024/9/28 17:29:37

计算器

文章目录

  • 计算器
    • 前言
    • 简单的四则运算
    • UI界面
    • 事件的逻辑
    • 小结

前言

笔者应组内要求,简单实现了一个可以完成简单四则运算的计算器程序。UI界面则是通过最近学习的Masonry库来实现的,而简单的四则运算内容则是通过栈来实现一个简单的四则运算。

简单的四则运算

笔者这里四则运算的思路是一个中缀表达式转后缀表达式的方式,然后再通过后缀表达式来进行一个计算,然后得到一个结果。这里中缀表达式转后缀表达式的思路主要参考这篇博客《数据结构》:中缀表达式转后缀表达式 + 后缀表达式的计算

这里简单说明一下我们为什么在计算机中要将中缀表达式转换成后缀表达式,中缀表达式的顺序是混乱的(因为有括号和每个符号优先级的问题),而转化成后缀表达式的逻辑就会变得很简单,我们只用按照栈中的顺序来进行一个运算就可以了。

中缀表达式转后缀表达式的核心思想其实就是对于我们的运算符的顺序的控制,如果遇到右括号的话,我们要一直让符号栈一直出栈直到遇到左括号才停止。遇到操作符的话,我们只需要满足下面这个条件就可以了,栈为空或者是我们的当前的操作符的优先级大于栈顶元素的操作符时候,我们的操作符栈就可以停止出栈了,然后给当前读到的操作符入栈。

对于数字我们都是进行一个直接入栈。

这里给出一个C语言版本:

typedef struct Stack {
    char stk[80];
    int top;
}Stack;
int EmptyStack(Stack* stk) {
    if (stk->top == -1) {
        return 1;
    } else {
        return 0;
    }
}
char getTopStack(Stack* stk) {
    if (EmptyStack(stk)) {
        return -1;
    } else {
        return stk->stk[stk->top];
    }
}
int fullStack(Stack* stack) {
    if (stack->top == 80) {
        return 1;
    } else {
        return 0;
    }
}
void pushStack(Stack* stack, char a) {
    if (fullStack(stack)) {
        return;
    } else {
        stack->stk[++stack->top] = a;
    }
}
char popStack(Stack* stack) {
    if (EmptyStack(stack)) {
        return -1;
    } else {
        return stack->stk[stack->top--];
    }
}
int isDigit(char a) {
    int flag;
    switch (a) {
        case '0':
            flag = 1;
            break;
        case '1':
            flag = 1;
            break;
        case '2':
            flag = 1;
            break;
        case '3':
            flag = 1;
            break;
        case '4':
            flag = 1;
            break;
        case '5':
            flag = 1;
            break;
        case '6':
            flag = 1;
            break;
        case '7':
            flag = 1;
            break;
        case '8':
            flag = 1;
            break;
        case '9':
            flag = 1;
            break;
        default:
            flag = 0;
            break;
    }
    return flag;
}
char** changeStack(Stack* stk, int length, char* s, int* num1) {
    char** string = (char**)malloc(sizeof(char*) * 30);
    for (int i = 0; i < 30; i++) {
        string[i] = (char*)malloc(sizeof(char) * 10);
    }
    int num = 0;
    int tail = 0;
    for (int i = 0; i < length; i++) {
        if (s[i] == '(') {
            pushStack(stk, s[i]);
        } else if (s[i] == ')') {
            if (tail > 0) {
                string[num][tail] = '\0';
                num++;
                tail = 0;
            }
            while (!EmptyStack(stk) && getTopStack(stk) != '(') {
                string[num][0] = popStack(stk);
                string[num][1] = '\0';
                num++;
            }
            popStack(stk);
        } else if (isDigit(s[i]) || s[i] == '.') {
            string[num][tail++] = s[i];
        } else if (s[i] == '+' || s[i] == '-') {
            if (i == 0 || (i > 0 && !isDigit(s[i - 1]) && s[i - 1] != ')' && s[i] == '-')) {
                string[num][tail++] = s[i];
            } else {
                if (tail > 0) {
                    string[num][tail] = '\0';
                    num++;
                    tail = 0;
                }
                while (!EmptyStack(stk) && (getTopStack(stk) == '*' || getTopStack(stk) == '/' || getTopStack(stk) == '+' || getTopStack(stk) == '-')) {
                    string[num][0] = popStack(stk);
                    string[num][1] = '\0';
                    num++;
                }
                pushStack(stk, s[i]);
            }
        } else if (s[i] == '*' || s[i] == '/') {
            if (tail > 0) {
                string[num][tail] = '\0';
                num++;
                tail = 0;
            }
            while (!EmptyStack(stk) && (getTopStack(stk) == '*' || getTopStack(stk) == '/')) {
                string[num][0] = popStack(stk);
                string[num][1] = '\0';
                num++;
            }
            pushStack(stk, s[i]);
        }
    }
    if (tail > 0) {
        string[num][tail] = '\0';
        num++;
    }
    while (!EmptyStack(stk)) {
        string[num][0] = popStack(stk);
        string[num][1] = '\0';
        num++;
    }
    *num1 = num;
    return string;
}
int isNumber(char* token) {
    return strlen(token) > 1 || ('0' <= token[0] && token[0] <= '9');
}
double change(char* token) {
    double x = 0;
    double decimalFactor = 1.0;
    int index = -1;
    int flag = 1;
    if (token[0] == '-') {
        flag = -1;
    }
    for (int i = 0; i < strlen(token); i++) {
        if (token[i] == '-') {
            continue;
        }
        if (token[i] == '.') {
            index = i;
        } else {
            if (index == -1) {
                x = x * 10 + (token[i] - '0');
            } else {
                decimalFactor *= 0.1;
                x += (token[i] - '0') * decimalFactor;
            }
        }
    }
    printf("%lf\n", x * flag);
    return x * flag;
}
double evalRPN(char** tokens, int tokensSize) {
    int n = tokensSize;
    double stk[n];
    int top = 0;
    for (int i = 0; i < n; i++) {
        char* token = tokens[i];
        if (strlen(token) == 0) {
            continue;
        }
        if (isNumber(token)) {
            stk[top++] = change(token);
        } else {
            double num2 = stk[--top];
            double num1 = stk[--top];
            switch (token[0]) {
                case '+':
                    stk[top++] = num1 + num2;
                    break;
                case '-':
                    stk[top++] = num1 - num2;
                    break;
                case '*':
                    stk[top++] = num1 * num2;
                    break;
                case '/':
                    stk[top++] = num1 / num2;
                    break;
            }
        }
    }
    return stk[top - 1];
}

这里和上面简单的版本有一点区别,这里的还考虑到了一个负数的判别和一个小数点的时候对于我们的数字的一个读取特别判断,这里如果是数字或者是一个小数点我们都要继续进行一个读取。这里我对于负数的处理是将负号存储到我们对应的数字前面,因为一个数字如果是负数的话,那他的负号是链接在运算符后面的,或者链接在左括号后面的。所以通过一个特判,来分辨我们的普通符号减和一个负数的标志。

UI界面

在这里插入图片描述

UI界面采用了Masonry来布局,这个界面大致有两个部分组成一个是我们的textField,剩下的部分则是我们的按钮部分,这里布局我采用了一个for循环来不断创建我们的button,并且给这些button赋值对应的tag,这样方便我们对于具有不同button的进行一个划分。

UIView* preView = nil;
for (int i = 0; i < 19; i++) {
        UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        [self addSubview:button];
        //button.backgroundColor = UIColor.whiteColor;
        [button setTitle:ary[i] forState:UIControlStateNormal];
        [button setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
        button.titleLabel.font = [UIFont systemFontOfSize:37];
        button.tag = 100 + i;
        if (i == 0) {
            [button mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(self).offset(20);
                make.top.equalTo(self.textField.mas_bottom).offset(10);
                make.size.equalTo(@80);
            }];
        } else if (i % 4 == 0 && i != 16) {
            [button mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(self).offset(20);
                make.top.equalTo(preView.mas_bottom).offset(10);
                make.size.equalTo(@80);
            }];
        } else if (i == 16) {
            [button mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(self).offset(20);
                make.top.equalTo(preView.mas_bottom).offset(10);
                make.width.equalTo(@170);
                make.height.equalTo(@80);
            }];
        } else {
            [button mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(preView.mas_right).offset(10);
                make.top.equalTo(preView);
                make.size.equalTo(@80);
            }];
        }
        button.layer.cornerRadius = 80 / 2;
        button.layer.masksToBounds = YES;
        preView = button;
    }
    for (UIView* subview in self.subviews) {
        if ([subview isKindOfClass:[UIButton class]]) {
            if (subview.tag < 103) {
                subview.backgroundColor = UIColor.lightGrayColor;
            } else if (subview.tag == 103 || subview.tag == 107 || subview.tag == 111 || subview.tag == 115 || subview.tag == 118) {
                subview.backgroundColor = UIColor.orangeColor;
            } else {
                subview.backgroundColor = UIColor.darkGrayColor;
            }
        }
    }

这部分代码是一个创建button的代码,然后根据button的不同tag来分配颜色以及设置对应的位置。

因为采用MVC架构,所以我这里将所有给button添加事件的函数都放在了ViewController中。

 for (UIView* subview in _myView.subviews) {
        if ([subview isKindOfClass:[UIButton class]]) {
            UIButton* myButton = (UIButton*)subview;
            if (subview.tag == 100) {
                [myButton addTarget:self action:@selector(empty) forControlEvents:UIControlEventTouchUpInside];
            } else if (subview.tag == 103 || subview.tag == 102 || subview.tag == 101 || subview.tag == 107 || subview.tag == 111 || subview.tag == 115 || subview.tag == 117) {
                [myButton addTarget:self action:@selector(pressopator:) forControlEvents:UIControlEventTouchUpInside];
            } else if (subview.tag == 118) {
                [myButton addTarget:self action:@selector(pressEqual:)
                   forControlEvents:UIControlEventTouchUpInside];
                NSLog(@"12");
            } else {
                [myButton addTarget:self action:@selector(pressNum:) forControlEvents:UIControlEventTouchUpInside];
            }
        }
    }

这部分实现了一个给button添加事件函数。

这里可以注意一下textfieldadjustsFontSizeToFitWidth属性可以让他根据字符串长度来实现一个自适应字体的效果。

在这里插入图片描述

事件的逻辑

这里笔者对于输入运算符做了限制,同时也对我们输入的小数点和左右括号都做了限制。

比方说笔者在一开始只允许我们的负号输入和左括号允许输入,别的操作符被设置成无法键入符号的状态。

又或者是在输入数字的时候限制他只能输入一个小数点。

这部分的逻辑其实比较复杂,要考虑的内容也比较多。比方说判断数字的小数点个数是否符合要求或者是判断多个运算符重叠的情况。

在这里插入图片描述

这里我主要把这部分的判断分成了两部分,一个是通过一些全局变量来控制一些不合理的输入,另一个则是通过判断中缀表达式是否合理来然后返回一个error字符串。

这里我是通过一个dotFlag和numFlag来控制他一个数字只能输入一次小数点,从而限制输入。另一个部分就是我们开始我设置成只可以输入的符号只有负号。

小结

计算器的仿写比较困难的点在于我们需要考虑的问题比较多,以及对于字符串的处理需要注意一下。细节地方比较多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2174230.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

只需要两步制作GIF动态图,方便快捷,制作动态表情包的利器!

推荐阅读&#xff1a;Python制作进度条&#xff0c;18种方式全网最全&#xff01;&#xff08;不全去你家扫厕所&#xff01;&#xff09; 在日常生活中肯定会接触到gif&#xff0c;例如在写文章的时候&#xff0c;有时需要将自己的代码的运行结果展示出来&#xff0c;如果放一…

面试遇到的质量体系10个问题(深度思考)

在某大型公司的招聘面试中关于质量体系本身及建设实践方面的10个问题&#xff0c;这些问题都是偏理论性强一些&#xff0c;但是可以通过这些问题来了解大型公司对质量体系的一些想法和预期的内容&#xff0c;本期先抛出来这10个问题&#xff0c;不附答案&#xff0c;目的就是让…

浏览器用户行为集群建设-数仓建模-数据计算

项目介绍 该项目旨在将集群构建--数仓建模--数据计算通路进行模拟&#xff0c;以达到熟悉整个数据流程的效果。 该项目模拟浏览器后台数据集群身份&#xff0c;收集用户浏览器访问数据传入数据集群&#xff0c;并进行数仓建模&#xff0c;以此基础进行相关计算和看数。 该项…

浅谈域攻防渗透之道-凭据获取

静时修止动修观&#xff0c;历历情人挂眼前&#xff1b;若把此心以学道&#xff0c;即身成佛有何难&#xff1f; 前言 通过提权得到了⼀个⾼权限的⽤户身份&#xff0c;例如获取到了 SYSYEM 权限后&#xff0c;就可以抓当前机器上各类密码&#xff1a;机器密码、浏览器密码、…

asynDriver-2

操作理论 初始化 在初始化中&#xff0c;端口驱动注册每个通信端口以及所有支持的接口。 用户代码创建一个asynUser, 它是访问asynDriver功能的"句柄"&#xff0c;通过调用&#xff1a; pasynManager->createAsynUser(processCallback,timeoutCallback); 一个…

基于单片机语音智能导盲仪仿真设计

文章目录 前言资料获取设计介绍设计程序具体实现截图设计获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们…

VulnHub-SickOs1.1靶机笔记

SickOs1.1靶机笔记 概述 Vulnhub的靶机sickos1.1 主要练习从互联网上搜取信息的能力&#xff0c;还考察了对代理使用&#xff0c;目录爆破的能力&#xff0c;很不错的靶机 靶机地址&#xff1a; 链接: https://pan.baidu.com/s/1JOTvKbfT-IpcgypcxaCEyQ?pwdytad 提取码: yt…

AFSim仿真系统 --- 系统简解_02(向导模块)

向导 向导是AFSIM的集成开发环境。它提供了视觉和基于文本的工具&#xff0c;以简化场景的开发和执行。 向导支持嵌入式执行基于文本的WSF应用程序&#xff0c;例如任务和传感器图&#xff0c;并提供快捷方式以方便启动其他WSF视觉应用程序&#xff0c;如Warlock和Mystic。 核…

图解IRF

FW1 配置思路 ① 配置IRF优先级 确认设备的主次 ② 设置批量操作的接口方便后续操作 interface range name fw-irf interface GigabitEthernet1/0/2 to GigabitEthernet1/0/3 ③ 接口 showdown 关闭接口 ④ 创建的IRF 1/1 成员的对应的接口的是 GE1/0/2 GE/1/0/3 ⑤ 开放IRF对…

Mathematica线性优化-单纯形/改善单纯形/内点法

引言 Mathematica提供了多种工具和函数来实现线性优化&#xff0c;这些工具可以处理从简单的线性规划问题到复杂的多变量优化问题&#xff0c;最近运筹学作业要熟悉线性优化的编程方法&#xff0c;我们就使用mathematica进行&#xff1a;所有运行代码都在文章上面的资源中&…

Python | Leetcode Python题解之第435题无重叠区间

题目&#xff1a; 题解&#xff1a; class Solution:def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:if not intervals:return 0intervals.sort(keylambda x: x[1])n len(intervals)right intervals[0][1]ans 1for i in range(1, n):if intervals…

c++速成 01 数据类型与基本运算符

文章目录 前言整型整型短整型长整型无符号整型 浮点型单精度双精度长双精度 变量命名规则&#xff1a;局部变量 全局变量基本运算符算术运算符&#xff1a;赋值运算符比较运算符逻辑运算符位运算符杂项运算符运算符间的优先级 前言 写在前面&#xff1a;本笔记参考b站视频【《…

从零开始手写STL库:Stack

从零开始手写STL库–Stack的实现 Gihub链接&#xff1a;miniSTL 文章目录 从零开始手写STL库–Stack的实现一、stack是什么&#xff1f;二、stack要包含什么函数总结 一、stack是什么&#xff1f; 栈是一种后进先出&#xff08;LIFO&#xff0c;Last In First Out&#xff09…

前端常用动画 直接可以用的代码加详细流程和案例 能应付90%的开发场景

前端项目&#xff0c;特别是Toc的项目&#xff0c;一定少不了各种动效和动画效果。 葫芦七兄弟&#xff1a; CSS 动画 优点&#xff1a;兼容性强&#xff1b;浏览器针对的流畅度优化&#xff1b;语法简单&#xff1b;某些属性&#xff08;如 transform 和 opacity&#xff09;…

CSS 的背景样式

1.1 背景颜色 1.2 背景图片 1.3 背景平铺 1.4 背景图片位置 1.4.1 方位名词 1.4.2 精确单位 1.4.3 混合单位 1.5 背景图像固定 1.6 背景复合写法 1.7 背景色半透明 1.8 总结

Json-Rpc框架(Muduo库快速上手)

阅读导航 引言一、Muduo库简介二、Muduo库常见接口1. TcpServer类基础介绍2. EventLoop类基础介绍3. TcpConnection类基础介绍4. TcpClient类基础介绍5. Buffer类基础介绍 三、Muduo库使用示例⭕英译汉服务器⭕英译汉客户端 引言 在上一篇文章中&#xff0c;我们简要介绍了在项…

业务资源管理模式语言19

相关模式&#xff1a; 如果你考虑类“Resource Maintenance”和“Part used in maintenance”&#xff0c;那么是“Transaction-Transaction Line Item”模式的一个特例[Coa 97]。如果你考虑类“Part”和“Part used in maintenance”&#xff0c;那么是“Item Line Item”模式…

力扣 简单 104.二叉树的最大深度

文章目录 题目介绍解法 题目介绍 解法 如果知道了左子树和右子树的最大深度 l 和 r&#xff0c;那么该二叉树的最大深度即为max(l,r)1&#xff0c;而左子树和右子树的最大深度又可以以同样的方式进行计算。因此我们可以用递归的方法来计算二叉树的最大深度。具体而言&#xff…

动态规划(有背包问题)

目录 1.动态规划的介绍 2.动态规划的例题 第1道题 数字三角形 (如果想看递归写法可以到我的记忆化递归里去看看记忆化递归_将递归程序记忆化-CSDN博客) 第2道题最长公共子序列(模板) 第3道题 最长上升子序列 第4道题最大子段和 背包系列问题 01背包 完全背包 1.动态规…

scrapy爬虫基础

一、初识 创建项目&#xff1a; scrapy startproject my_one_project # 创建项目命令 cd my_one_project # 先进去&#xff0c; 后面在里面运行 运行爬虫命令为&#xff1a;scrapy crawl tk spiders下创建test.py 其中name就是scrapy crawl tk &…