当前位置: 首页 > news >正文

【OC】计算器的仿写

文章目录

  • 前言
  • C语言实现的简单四则运算
    • 代码示例
  • 计算器
    • View界面层
      • 代码示例
    • Controller层
      • 按钮点击函数
    • Model层
  • 总结

前言

最近简单完成了一个简单的计算器的仿写,里面包含了对Masonry库布局和对MVC的使用,m层主要是用类似于c语言的栈的操作进行数据的处理,v层是使用Masonry进行布局的设计

C语言实现的简单四则运算

我对四则运算的思路是先把我们输入的中缀表达式转为后缀,再对后缀表达式进行计算的思想,因为计算机无法正确识别我们正常的中缀表达式,且无法识别正常的运算优先级,如果是后缀的话可以直接用栈来处理里面的逻辑,进行转换的主要思路可以参考下面这篇博客《数据结构》:中缀表达式转后缀表达式 + 后缀表达式的计算,

代码示例

typedef struct Stack {char stack[100];int top;
} Stack;
int EmptyStack(Stack* stack) {return stack->top == -1;
}
int fullStack(Stack* stack) {return stack->top == 99;
}
void pushStack(Stack* stack, char a) {if (!fullStack(stack)) {stack->stack[++stack->top] = a;}
}
char getTopStack(Stack* stack) {return EmptyStack(stack) ? -1 : stack->stack[stack->top];
}
char popStack(Stack* stack) {if (EmptyStack(stack)) {return -1;} else {char val = stack->stack[stack->top];stack->top--;return val;}
}
int isDigit(char a) {return a >= '0' && a <= '9';
}char** inToPostfix(Stack* stack, char* s, int* num1) {int length = (int)strlen(s);char** output = (char**)malloc(sizeof(char*) * 30);for (int i = 0; i < 30; i++) {output[i] = (char*)malloc(sizeof(char) * 10);}int num = 0, tail = 0;for (int i = 0; i < length; i++) {if (s[i] == '(') {pushStack(stack, s[i]);} else if (s[i] == ')') {if (tail > 0) {output[num][tail] = '\0';num++;tail = 0;}while (!EmptyStack(stack) && getTopStack(stack) != '(') {output[num][0] = popStack(stack);output[num][1] = '\0';num++;}popStack(stack);} else if (isDigit(s[i]) || s[i] == '.') {output[num][tail++] = s[i];} else if (s[i] == '+' || s[i] == '-') {if (s[i] == '-' && (i == 0 || (!isDigit(s[i - 1]) && s[i - 1] != ')'))) {if (i + 1 < length && s[i + 1] == '(') {strcpy(output[num++], "0");pushStack(stack, '-');} else {output[num][tail++] = s[i];while (i + 1 < length && (isDigit(s[i + 1]) || s[i + 1] == '.')) {output[num][tail++] = s[++i];}output[num][tail] = '\0';num++;tail = 0;}} else {if (tail > 0) {output[num][tail] = '\0';num++;tail = 0;}while (!EmptyStack(stack) &&(getTopStack(stack) == '*' || getTopStack(stack) == '/' ||getTopStack(stack) == '+' || getTopStack(stack) == '-')) {output[num][0] = popStack(stack);output[num][1] = '\0';num++;}pushStack(stack, s[i]);}} else if (s[i] == '*' || s[i] == '/') {if (tail > 0) {output[num][tail] = '\0';num++;tail = 0;}while (!EmptyStack(stack) && (getTopStack(stack) == '*' || getTopStack(stack) == '/')) {output[num][0] = popStack(stack);output[num][1] = '\0';num++;}pushStack(stack, s[i]);}}if (tail > 0) {output[num][tail] = '\0';num++;}while (!EmptyStack(stack)) {output[num][0] = popStack(stack);output[num][1] = '\0';num++;}*num1 = num;return output;
}
int isNumber(char* token) {return strlen(token) > 1 || (token[0] >= '0' && token[0] <= '9');
}
double changeToMath(char* token) {double x = 0, decdouble = 1.0;int index = -1, 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 {decdouble *= 0.1;x += (token[i] - '0') * decdouble;}}}return x * flag;
}
double evalRPN (char** tokens, int tokenSize) {double stack[100];int top = 0;for (int i = 0; i < tokenSize; i++) {if (strlen(tokens[i]) == 0) {continue;}if (isNumber(tokens[i])) {stack[top++] = changeToMath(tokens[i]);} else {double b = stack[--top];double a = stack[--top];switch (tokens[i][0]) {case '+':stack[top++] = a + b;break;case '-':stack[top++] = a - b;break;case '*':stack[top++] = a * b;break;case '/':stack[top++] = a / b;break;default:break;}}}return stack[top - 1];
}
int main(int argc, const char * argv[]) {@autoreleasepool {Stack stack;stack.top = -1;char expr[100];printf("请输入算式:");scanf("%s", expr);int num1;char** tokens = inToPostfix(&stack, expr, &num1);double result = evalRPN(tokens, num1);printf("%.6lf", result);}return 0;
}

主要的计算结构实现过程:中缀表达式解析(inToPostfis)->逆波兰表达式(tokens)->后缀求值(evalRPN)

并且对含有负号时进行了特别判断

if (s[i] == '-' && (i == 0 || (!isDigit(s[i - 1]) && s[i - 1] != ')')))

这里主要是据上下文语义的不同对负号的特别判断

计算器

View界面层

主要是使用masonry进行布局,据设置的输入框进行下面相对位置的设置

请添加图片描述

代码示例

//下面就是先进行对话框及主页面的初始化,并调用按钮的设置函数后面设置其他控件
self = [super initWithFrame: frame];self.backgroundColor = [UIColor blackColor];if (self) {UITextField *displaytext = [[UITextField alloc] init];displaytext.textAlignment = NSTextAlignmentRight;displaytext.font = [UIFont systemFontOfSize: 50 weight: UIFontWeightMedium];displaytext.textColor = [UIColor whiteColor];displaytext.text = @"0";displaytext.adjustsFontSizeToFitWidth = YES;displaytext.layer.cornerRadius = 10;displaytext.layer.masksToBounds = YES;displaytext.enabled = NO;[self addSubview: displaytext];self.textField = displaytext;[displaytext mas_makeConstraints:^(MASConstraintMaker *make) {make.left.equalTo(self).offset(20);make.right.equalTo(self).offset(-20);make.top.equalTo(self).offset(240);make.height.equalTo(@100);}];[self setupButtons];}
//下面主要是各个按钮的设置逻辑,最后点击之后的返回逻辑设置在C层里
NSArray *ary = @[@"AC", @"(", @")", @"/",@"7", @"8", @"9", @"*",@"4", @"5", @"6", @"-",@"1", @"2", @"3", @"+",@"0", @".", @"="];UIView *preView = nil;for (int i = 0; i < ary.count; i++) {UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];[self addSubview:button];[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 addTarget: self action: @selector(buttonTapped:) forControlEvents: UIControlEventTouchUpInside];button.layer.cornerRadius = 40;button.layer.masksToBounds = YES;preView = button;}

Controller层

这里主要是对从view层中点击后逻辑的建立,以及处理从M层中对数据处理计算之后返回的错误或者其正确信息的返回

请添加图片描述

按钮点击函数

- (void)handleButton:(NSString *)title {NSString *text = self.calcuView.textField.text;if (([text isEqualToString: @"Error"] && ![title isEqualToString: @"AC"]) || ([text isEqualToString: @"除以0"] && ![title isEqualToString: @"AC"])) {self.calcuView.textField.text = @"";text = @"";}if ([title isEqualToString: @"AC"]) {self.calcuView.textField.text = @"0";return;}if ([title isEqualToString:@"="]) {[self calculateExpression:text];return;}if ([title isEqualToString: @")"]) {NSUInteger leftCount = [[text componentsSeparatedByString: @"("]count] - 1;NSUInteger rightCount = [[text componentsSeparatedByString: @")"]count] - 1;if (rightCount >= leftCount) {return;}}if (text.length == 0 || [text isEqualToString:@"0"]) {if ([title isEqualToString:@"-"]) {self.calcuView.textField.text = @"-";return;}if ([title isEqualToString:@"."]) {self.calcuView.textField.text = @"0.";return;}if ([self isOperator:title]) {return;}self.calcuView.textField.text = title;return;}NSString *lastchar = [text substringFromIndex:text.length - 1];if ([self isOperator:lastchar] && [self isOperator:title]) {if ([title isEqualToString:@"."]) {if (![lastchar isEqualToString:@"."] && ![self lastNumberHasDot:text]) {self.calcuView.textField.text = [text stringByAppendingString:title];}}return;}if ([title isEqualToString:@"."]) {if ([self lastNumberHasDot:text]) {return;}}if ([text isEqualToString:@"0"] && [@[@"+",@"-",@"*",@"/",@"."] containsObject:title]) {return;}if ([text isEqualToString:@"0"] && ![title isEqualToString: @"."]) {self.calcuView.textField.text = title;} else {self.calcuView.textField.text = [text stringByAppendingString: title];}
}

这里主要处理点击按钮后部分逻辑的确立,及各种对错误行为的限制,如识别前面左括号的个数,相应的规范右括号的最多数量,开始时不能输入除负号及左括号以外的一些符号,如上面第二次无法继续添加多余括号就是在这里规定的

- (void)calculateExpression:(NSString *)expr {@try {const char *cExpr = [expr UTF8String];double value = evalExpression(cExpr);self.calcuView.textField.text = [NSString stringWithFormat:@"%g", value];}@catch (NSException *exception) {if ([exception.reason isEqualToString: @"除以0"]) {self.calcuView.textField.text = @"除以0";} else {self.calcuView.textField.text = @"Error";}}
}

这里是对一些错误的判断的返回,并且使用try-catch语法,同时也可以捕捉m数据层返回的一些错误

Model层

这里的大部分逻辑跟前面提到的用c语言写的四则运算的逻辑差不多,主要还是在各个地方都加入了@throw进行错误的抛出,从而能在Controller中检测出,最后返回View层展示出来

这里还使用到了isnan和isinf,用于判断返回数是不是有效数与是不是除以0了(即是否为无穷大)

double stack[100];int top = 0;for (int i = 0; i < tokenSize; i++) {if (tokens[i] == NULL) {continue;}if (strlen(tokens[i]) == 0) {continue;}if (isNumber(tokens[i])) {stack[top++] = changeToMath(tokens[i]);} else {if (top < 2) {@throw [NSException exceptionWithName: @"Error" reason: nil userInfo:nil];}double b = stack[--top];double a = stack[--top];switch (tokens[i][0]) {case '+':stack[top++] = a + b;break;case '-':stack[top++] = a - b;break;case '*':stack[top++] = a * b;break;case '/':if (b == 0) {@throw [NSException exceptionWithName:@"Error" reason: @"除以0" userInfo:nil];}stack[top++] = a / b;break;default:break;}}}return stack[top - 1];

请添加图片描述

总结

计算器其实内容不是非常多,但是内部需要限制及处理的各种细枝末节相当多,需要反复进行调试,不然程序非常容易就会崩溃

http://www.dtcms.com/a/485773.html

相关文章:

  • 东莞工厂网站建设网站建设要买哪些软件
  • 5-3〔OSCP ◈ 研记〕❘ SQL注入攻击▸基于错误的SQLi 盲注SQLi
  • AWS Redshift 数据仓库完整配置与自动化管理指南
  • 《C++ 手搓list容器底层》:从结构原理深度解析到功能实现(附源码版)
  • 成都那家做网站好注册网约车主需要什么条件
  • Wireshark:HTTP、MQTT、WebSocket 抓包详细教程
  • Linux内核架构浅谈36-Linux页帧描述:struct page数据结构的设计与关键成员
  • 道路车辆功能安全标准(FuSa)基础(七)
  • 【Linux系列】解码 Linux 内存地图:从虚拟到物理的寻宝之旅
  • vue+spring boot 利用ffmpeg实现大视频切片
  • 长沙手机网站建设公司wordpress 做笔记
  • Java基于Web3j调用智能智能合约案例
  • 关于联想ThinkCentre M950t-N000 M大师电脑恢复预装系统镜像遇到的一点问题
  • 有关优化网站建设的书籍深圳网络推广方法
  • 招聘网站做竞品分析南昌网站seo多少钱
  • 【实战总结】Docker部署MySQL完整教程:附docker-compose模板与常用命令大全
  • C++ string类的使用
  • 【数据结构】:C 语言常见排序算法的实现与特性解析
  • C语言数据结构:算法复杂度(1)
  • 16km无人机WiFi中继图传模块,高速传输画质高清不卡顿
  • Linux系统C++开发环境搭建工具(二)—— etcd 使用指南
  • AI+大数据时代:如何从架构到生态重构时序数据库的价值?
  • 小小 Postgres,何以替代 Redis、MongoDB 甚至 ES?
  • Win10正式谢幕!附最后更新版本
  • 前端自动翻译插件webpack-auto-i18n-plugin的使用
  • 山东官方网站建设沧州网络推广渠成网络
  • 贺州网站建设公司家装设计需要学什么软件
  • 网站在百度上搜索不到丽水山耕品牌建设网站
  • 漂亮的门户网站dedecms游戏门户网站源码
  • thinkphp2.1网站挂文件国有企业投资建设项目