【iOS】计算器的仿写

计算器

文章目录

  • 计算器
    • 前言
    • 简单的四则运算
    • 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];
}

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

但是在OC中给出了一个类NSDecimalNumber这个类可以实现一个比较精确的加减乘除,下面给出我们使用这个类来实现计算的过程

- (NSDecimalNumber*) evalRPN {
    NSInteger n = self.ary.count;
    NSLog(@"%@", self.ary);
    NSMutableArray* stack = [NSMutableArray array];
    //int top = 0;
    
    for (int i = 0; i < n; i++) {
        NSString* token = self.ary[i];
        if (token.length == 0) {
            continue;
        }
        if ([self isNumber:token]) {
            [stack addObject: [self change:token]];
        } else {
            NSDecimalNumber* num2 = [stack lastObject];
            [stack removeLastObject];
            NSDecimalNumber* num1 = [stack lastObject];
            [stack removeLastObject];
            if ([token isEqualToString:@"+"]) {
                [stack addObject:[num1 decimalNumberByAdding:num2]];
            } else if ([token isEqualToString:@"-"]) {
                [stack addObject:[num1 decimalNumberBySubtracting:num2]];
            } else if ([token isEqualToString:@"×"]) {
                [stack addObject:[num1 decimalNumberByMultiplyingBy:num2]];
            } else if ([token isEqualToString:@"÷"]) {
                [stack addObject:[num1 decimalNumberByDividingBy:num2]];
            }
        }
    }
    if (stack.count > 1) {
        return nil;
    } else {
        return [stack lastObject];
    }
}

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.mfbz.cn/a/886205.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Halcon实用系列1-识别二维条码

在做项目时&#xff0c;之前使用的是某康的智能读码器&#xff0c;综合考虑成本&#xff0c;可通过相机拍照来读取图片的二维码&#xff0c;我这边用Halcon来实现。 Halcon代码如下&#xff1a; *创建模型 create_data_code_2d_model(Data Matrix ECC 200, [], [], DataCodeH…

5G NR物理信道简介

文章目录 NR 上行物理信道PRACHPUCCHPUSCH NR 下行物理信道PBCHPDCCHPDSCH NR 上行物理信道 PRACH PRACH&#xff08;Physical Random Access Channel)物理随机接入信道&#xff0c;用于传导preamble 序列。PRACH 由循环前缀CP、前导序列和保护间隔三部分组成。 PUCCH PUCCH…

【AI大模型】深入Transformer架构:编码器部分的实现与解析(上)

目录 &#x1f354; 编码器介绍 &#x1f354; 掩码张量 2.1 掩码张量介绍 2.2 掩码张量的作用 2.3 生成掩码张量的代码分析 2.4 掩码张量的可视化 2.5 掩码张量总结 &#x1f354; 注意力机制 3.1 注意力计算规则的代码分析 3.2 带有mask的输入参数&#xff1a; 3.…

FOCShield v2.0.4原理图

1.FOCShield v2.0.4原理图,开源原文件用AD制作。用 AD09可以打开。 主要部分为 1.电机驱动芯片部分 2.电流采样部分

Windows 环境下安装 Anaconda 并适配到 PowerShell 的保姆级教程

Anaconda Anaconda 是一个流行的 Python 数据科学和机器学习平台&#xff0c;它包括了 Conda 包管理器、Python 以及数百个用于科学计算的库和工具。Anaconda 旨在简化包和环境管理&#xff0c;使得安装、更新和管理软件包变得容易&#xff0c;同时也能够轻松创建和切换不同的P…

大数据毕业设计选题推荐-民族服饰数据分析系统-Python数据可视化-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

【Linux】命令管道

一、命名管道的介绍 之前的管道博客中介绍的是匿名管道&#xff0c;这个管道的应用的一个限制就是只能在具有公共祖先&#xff08;具有亲缘关系&#xff09;的进程间通信。 如果我们不想在不相关的进程之间交换数据&#xff0c;可以使用FIFO文件来做这项工作&#xff0c;他经常…

输入捕获模式测频率PWMI模式测频率占空比

前沿知识&#xff1a;TIM输入捕获-CSDN博客 输入捕获相关函数 // 初始化输入捕获单元 // ICInit是4个通道共用一个函数的&#xff0c;第二个结构体参数&#xff0c;可以用来配置具体是哪个通道。 void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);/…

【MAUI】CollectionView之 垂直网格

App主页或者导航页面中动态按钮的垂直网格布局 在 XAML 中,CollectionView 可以通过将其 ItemsLayout 属性设置为 VerticalGrid,在垂直网格中显示其项: <CollectionView ItemsSource="{Binding Monkeys}"ItemsLayout

TCP/UDP初识

TCP是面向连接的、可靠的、基于字节流的传输层协议。 面向连接&#xff1a;一定是一对一连接&#xff0c;不能像 UDP 协议可以一个主机同时向多个主机发送消息 可靠的&#xff1a;无论的网络链路中出现了怎样的链路变化&#xff0c;TCP 都可以保证一个报文一定能够到达接收端…

【YOLO学习】YOLOv2详解

文章目录 1. 概述2. Better2.1 Batch Normalization&#xff08;批归一化&#xff09;2.2 High Resolution Classifier&#xff08;高分辨率分类器&#xff09;2.3 Convolutional With Anchor Boxes&#xff08;带有Anchor Boxes的卷积&#xff09;2.4 Dimension Clusters&…

二分查找算法专题(1)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; 优选算法专题 目录 二分查找算法的介绍 704. 二分查找 34. 在排序数组中查找元素的第一个和 最后一个位置 35. 搜索插入位置 69. x的平…

力扣题解 983

大家好&#xff0c;欢迎来到无限大的判断&#xff0c;祝大家国庆假期愉快 题目描述&#xff08;中等&#xff09; 最低票价 在一个火车旅行很受欢迎的国度&#xff0c;你提前一年计划了一些火车旅行。在接下来的一年里&#xff0c;你要旅行的日子将以一个名为 days 的数组给出…

Charles(青花瓷)抓取https请求

文章目录 前言Charles&#xff08;青花瓷&#xff09;抓取https请求 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&…

kafka下载配置

下载安装 参开kafka社区 zookeeperkafka消息队列群集部署https://apache.csdn.net/66c958fb10164416336632c3.html 下载 kafka_2.12-3.2.0安装包快速下载地址分享 官网下载链接地址&#xff1a; 官网下载地址&#xff1a;https://kafka.apache.org/downloads 官网呢下载慢…

2024/10/2 408 20题

c d d b b a b c b b a d c d a c

java基础 day1

学习视频链接 人机交互的小故事 微软和乔布斯借鉴了施乐实现了如今的图形化界面 图形化界面对于用户来说&#xff0c;操作更加容易上手&#xff0c;但是也存在一些问题。使用图形化界面需要加载许多图片&#xff0c;所以消耗内存&#xff1b;此外运行的速度没有命令行快 Wi…

【华为HCIP实战课程一】OSPF相关基础介绍及基础配置,网络工程师必修

一、OSPF介绍 开放式最短路径优先协议OSPF(Open Shortest Path First),IPv4使用的OSPFv2,针对IPv6使用OSPFv3协议。 二、为什么需要OSPF OSPF出现之前,网络广泛使用RIP路由协议,RIP由于最大16跳数限制无法适应大型网络,RIP是基于距离矢量算法的路由协议,应用在大型网…

过去8年,编程语言的流行度发生了哪些变化?PHP下降,Objective-C已过时

前天有一个汇总9个不同排名数据的“地表最强”编程语言排行榜&#xff0c;为了更好地理解语言流行度的变化&#xff0c;作者将2016年的类似调查结果与2024年的数据进行了比较。 虽然2016年的调查只包含6个排名&#xff0c;但它仍然提供了宝贵的参考数据。 我们来看看详细的情…

JSON的C实现(上)

JSON的C实现&#xff08;上&#xff09; JSON的C实现&#xff08;上&#xff09;前言JSON简介JSON的C实现思路小结 JSON的C实现&#xff08;上&#xff09; 前言 JSON是众多项目中较为常见的数据交换格式&#xff0c;为不同项目、系统间的信息交换提供了一个规范化标准。JSON…