HarmonyOS实战开发-如何实现一个支持加减乘除混合运算的计算器。
介绍
本篇Codelab基于基础组件、容器组件,实现一个支持加减乘除混合运算的计算器。

说明: 由于数字都是双精度浮点数,在计算机中是二进制存储数据的,因此小数和非安全整数(超过整数的安全范围[-Math.pow(2, 53),Math.pow(2, 53)]的数据)在计算过程中会存在精度丢失的情况。
1、小数运算时:“0.2 + 2.22 = 2.4200000000000004”,当前示例的解决方法是将小数扩展到整数进行计算,计算完成之后再将结果缩小,计算过程为“(0.2 * 100 + 2.22 * 100) / 100 = 2.42”。
2、非安全整数运算时:“9007199254740992 + 1 = 9.007199254740992”,当前示例中将长度超过15位的数字转换成科学计数法,计算结果为“9007199254740992 + 1 = 9.007199254740993e15”。
相关概念
- ForEach组件:循环渲染组件**,**迭代数组并为每个数组项创建相应的组件。
- TextInput组件:单行文本输入框组件。
- Image组件:图片组件,支持本地图片和网络图片的渲染展示。
环境搭建
软件要求
- DevEco Studio版本:DevEco Studio 3.1 Release。
- OpenHarmony SDK版本:API version 9。
硬件要求
- 开发板类型:润和RK3568开发板。
- OpenHarmony系统:3.2 Release。
环境搭建
完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:
- 获取OpenHarmony系统版本:标准系统解决方案(二进制)。以3.2 Release版本为例:

2.搭建烧录环境。
- 完成DevEco Device Tool的安装
- 完成RK3568开发板的烧录
3.搭建开发环境。
- 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。
- 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”)。
- 工程创建完成后,选择使用真机进行调测。
代码结构解读
本篇Codelab只对核心代码进行讲解。
├──entry/src/main/ets // 代码区
│ ├──common
│ │ ├──constants
│ │ │ └──CommonConstants.ets // 公共常量类
│ │ └──util
│ │ ├──CalculateUtil.ets // 计算工具类
│ │ ├──CheckEmptyUtil.ets // 非空判断工具类
│ │ └──Logger.ets // 日志管理工具类
│ ├──entryability
│ │ └──EntryAbility.ts // 程序入口类
│ ├──model
│ │ └──CalculateModel.ets // 计算器页面数据处理类
│ ├──pages
│ │ └──HomePage.ets // 计算器页面
│ └──viewmodel
│ ├──PressKeysItem.ets // 按键信息类
│ └──PresskeysViewModel.ets // 计算器页面键盘数据
└──entry/src/main/resource // 应用静态资源目录
页面设计
页面由表达式输入框、结果输出框、键盘输入区域三部分组成,效果图如图:

表达式输入框位于页面最上方,使用TextInput组件实时显示键盘输入的数据,默认字体大小为“64fp”,当表达式输入框中数据长度大于9时,字体大小为“32fp”。
// HomePage.ets
Column() {TextInput({ text: this.model.resultFormat(this.inputValue) }).height(CommonConstants.FULL_PERCENT).fontSize((this.inputValue.length > CommonConstants.INPUT_LENGTH_MAX ?$r('app.float.font_size_text')) : $r('app.float.font_size_input')).enabled(false).fontColor(Color.Black).textAlign(TextAlign.End).backgroundColor($r('app.color.input_back_color'))
}
....
.margin({right: $r('app.float.input_margin_right'),top: $r('app.float.input_margin_top')
})
结果输出框位于表达式输入框下方,使用Text组件实时显示计算结果和“错误”提示,当表达式输入框最后一位为运算符时结果输出框中值不变。
// HomePage.ets
Column() {Text(this.model.resultFormat(this.calValue)).fontSize($r('app.float.font_size_text')).fontColor($r('app.color.text_color'))
}
.width(CommonConstants.FULL_PERCENT)
.height($r('app.float.text_height'))
.alignItems(HorizontalAlign.End)
.margin({right: $r('app.float.text_margin_right'),bottom: $r('app.float.text_margin_bottom')})
用ForEach组件渲染键盘输入区域,其中0~9、“.”、“%”用Text组件渲染;“+-×÷=”、清零、删除用Image组件渲染。
// HomePage.ets
ForEach(columnItem, (keyItem: PressKeysItem, keyItemIndex?: number) => {Column() {Column() {if (keyItem.flag === 0) {Image(keyItem.source !== undefined ? keyItem.source : '').width(keyItem.width).height(keyItem.height)} else {Text(keyItem.value).fontSize((keyItem.value === CommonConstants.DOTS) ?$r('app.float.font_size_dot') : $r('app.float.font_size_text')).width(keyItem.width).height(keyItem.height)}}.width($r('app.float.key_width')).height(((columnItemIndex === (keysModel.getPressKeys().length - 1)) &&(keyItemIndex === (columnItem.length - 1))) ?$r('app.float.equals_height') : $r('app.float.key_height'))....backgroundColor(((columnItemIndex === (keysModel.getPressKeys().length - 1)) &&(keyItemIndex === (columnItem.length - 1))) ?$r('app.color.equals_back_color') : Color.White)...}.layoutWeight(((columnItemIndex === (keysModel.getPressKeys().length - 1)) &&(keyItemIndex === (columnItem.length - 1))) ? CommonConstants.TWO : 1)...
}, (keyItem: PressKeysItem) => JSON.stringify(keyItem))
组装计算表达式
页面中数字输入和运算符输入分别调用inputNumber方法和inputSymbol方法。
// HomePage.ets
ForEach(columnItem, (keyItem: PressKeysItem, keyItemIndex?: number) => {Column() {Column() {...}....onClick(() => {if (keyItem.flag === 0) {this.model.inputSymbol(keyItem.value);} else {this.model.inputNumber(keyItem.value);}})}...)...
}, (keyItem: PressKeysItem) => JSON.stringify(keyItem))
说明: 输入的数字和运算符保存在数组中,数组通过“+-×÷”运算符将数字分开。 例如表达式为“10×8.2+40%÷2×-5-1”在数组中为["10", "×", "8.2", "+", "40%", "÷", "2", "×", "-5", "-", "1"]。 表达式中“%”为百分比,例如“40%”为“0.4”。
当为数字输入时,首先根据表达式数组中最后一个元素判断当前输入是否匹配,再判断表达式数组中最后一个元素为是否为负数。
// CalculateModel.ets
inputNumber(value: string) {...let len = this.expressions.length;let last = len > 0 ? this.expressions[len - 1] : '';let secondLast = len > 1 ? this.expressions[len - CommonConstants.TWO] : undefined;if (!this.validateEnter(last, value)) {return;}if (!last) {this.expressions.push(value);} else if (!secondLast) {this.expressions[len - 1] += value;}if (secondLast && CalculateUtil.isSymbol(secondLast)) {this.expressions[len -1] += value;}if (secondLast && !CalculateUtil.isSymbol(secondLast)) {this.expressions.push(value);}...
}// CalculateModel.ets
validateEnter(last: string, value: string) {if (!last && value === CommonConstants.PERCENT_SIGN) {return false;}if ((last === CommonConstants.MIN) && (value === CommonConstants.PERCENT_SIGN)) {return false;}if (last.endsWith(CommonConstants.PERCENT_SIGN)) {return false;}if ((last.indexOf(CommonConstants.DOTS) !== -1) && (value === CommonConstants.DOTS)) {return false;}if ((last === '0') && (value != CommonConstants.DOTS) &&(value !== CommonConstants.PERCENT_SIGN)) {return false;}return true;
}
当输入为“=”运算符时,将结果输入出框中的值显示到表达式输入框中,并清空结果输出框。当输入为“清零”运算符时,将页面和表达式数组清空。
// CalculateModel.ets
inputSymbol(value: string) {...switch (value) {case Symbol.CLEAN:this.expressions = [];this.context.calValue = '';break;...case Symbol.EQU:if (len === 0) {return;}this.getResult().then(result => {if (!result) {return;}this.context.inputValue = this.context.calValue;this.context.calValue = '';this.expressions = [];this.expressions.push(this.context.inputValue);})break;...}...
}
当输入为“删除”运算符时,若表达式数组中最后一位元素为运算符则删除,为数字则删除数字最后一位,重新计算表达式的值(表达式数组中最后一位为运算符则不参与计算),删除之后若表达式长度为0则清空页面。
// CalculateModel.ets
inputSymbol(value: string) {...switch (value) {...case CommonConstants.SYMBOL.DEL:this.inputDelete(len);break;...}...
}// CalculateModel.ets
inputDelete(len: number) {if (len === 0) {return;}let last = this.expressions[len - 1];let lastLen = last.length;if (lastLen === 1) {this.expressions.pop();len = this.expressions.length;} else {this.expressions[len - 1] = last.slice(0, last.length - 1);}if (len === 0) {this.context.inputValue = '';this.context.calValue = '';return;}if (!CalculateUtil.isSymbol(this.expressions[len - 1])) {this.getResult();}
}
当输入为“+-×÷”四则运算符时,由于可输入负数,故优先级高的运算符“×÷”后可输入“-”,其它场景则替换原有运算符。
// CalculateModel.ets
inputSymbol(value: string) {...switch (value) {...default:this.inputOperators(len, value);break;}...
}// CalculateModel.ets
inputOperators(len: number, value: string) {let last = len > 0 ? this.expressions[len - 1] : undefined;let secondLast = len > 1 ? this.expressions[len - CommonConstants.TWO] : undefined;if (!last && (value === Symbol.MIN)) {this.expressions.push(this.getSymbol(value));return;}if (!last) {return;}if (!CalculateUtil.isSymbol(last)) {this.expressions.push(this.getSymbol(value));return;}if ((value === Symbol.MIN) &&(last === CommonConstants.MIN || last === CommonConstants.ADD)) {this.expressions.pop();this.expressions.push(this.getSymbol(value));return;}if (!secondLast) {return;}if (value !== Symbol.MIN) {this.expressions.pop();}if (CalculateUtil.isSymbol(secondLast)) {this.expressions.pop();}this.expressions.push(this.getSymbol(value));
}
解析计算表达式
将表达式数组中带“%”的元素转换成小数,若表达式数组中最后一位为“+-×÷”则删除。
// CalculateUtil.ets
parseExpression(expressions: Array<string>): string {...let len = expressions.length;...expressions.forEach((item: string, index: number) => {// 处理表达式中的%if (item.indexOf(CommonConstants.PERCENT_SIGN) !== -1) {expressions[index] = (this.mulOrDiv(item.slice(0, item.length - 1),CommonConstants.ONE_HUNDRED, CommonConstants.DIV)).toString();}// 最后一位是否为运算符if ((index === len - 1) && this.isSymbol(item)) {expressions.pop();}});...
}
先初始化队列和栈,再从表达式数组左边取出元素,进行如下操作:
- 当取出的元素为数字时则放入队列中。
- 当取出的元素为运算符时,先判断栈中元素是否为空,是则将运算符放入栈中,否则判断此运算符与栈中最后一个元素的优先级,若此运算符优先级小则将栈中最后一个元素弹出并放入队列中,再将此运算符放入栈中,否则将此运算符放入栈中。
- 最后将栈中的元素依次弹出放入队列中。
// CalculateUtil.ets
parseExpression(expressions: Array<string>): string {...while (expressions.length > 0) {let current = expressions.shift();if (current !== undefined) {if (this.isSymbol(current)) {while (outputStack.length > 0 &&this.comparePriority(current, outputStack[outputStack.length - 1])) {let popValue: string | undefined = outputStack.pop();if (popValue !== undefined) {outputQueue.push(popValue);}}outputStack.push(current);} else {outputQueue.push(current);}}}while (outputStack.length > 0) {outputQueue.push(outputStack.pop());}...
}
以表达式“3×5+4÷2”为例,用原理图讲解上面代码,原理图如图:

遍历队列中的元素,当为数字时将元素压入栈,当为运算符时将数字弹出栈,并结合当前运算符进行计算,再将计算的结果压栈,最终栈底元素为表达式结果。
// CalculateUtil.ets
dealQueue(queue: Array<string>) {...let outputStack: string[] = [];while (queue.length > 0) {let current: string | undefined = queue.shift();if (current !== undefined) {if (!this.isSymbol(current)) {outputStack.push(current);} else {let second: string | undefined = outputStack.pop();let first: string | undefined = outputStack.pop();if (first !== undefined && second !== undefined) {let calResultValue: string = this.calResult(first, second, current)outputStack.push(calResultValue);}}}}if (outputStack.length !== 1) {return 'NaN';} else {let end = outputStack[0].endsWith(CommonConstants.DOTS) ?outputStack[0].substring(0, outputStack[0].length - 1) : outputStack[0];return end;}
}
获取表达式“3×5+4÷2”组装后的表达式,用原理图讲解上面代码,原理图如图:

总结
您已经完成了本次Codelab的学习,并了解到以下知识点:
- ForEach组件的使用。
- TextInput组件的使用。
- Image组件的使用。
为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→《HarmonyOS教学视频》
HarmonyOS教学视频:语法ArkTS、TypeScript、ArkUI等.....视频教程

鸿蒙生态应用开发白皮书V2.0PDF:
获取完整版白皮书请点击→《鸿蒙生态应用开发白皮书V2.0PDF》

鸿蒙 (Harmony OS)开发学习手册
一、入门必看
- 应用开发导读(ArkTS)
- ……

二、HarmonyOS 概念
- 系统定义
- 技术架构
- 技术特性
- 系统安全
- ........

三、如何快速入门?《做鸿蒙应用开发到底学习些啥?》
- 基本概念
- 构建第一个ArkTS应用
- ……

四、开发基础知识
- 应用基础知识
- 配置文件
- 应用数据管理
- 应用安全管理
- 应用隐私保护
- 三方应用调用管控机制
- 资源分类与访问
- 学习ArkTS语言
- ……

五、基于ArkTS 开发
- Ability开发
- UI开发
- 公共事件与通知
- 窗口管理
- 媒体
- 安全
- 网络与链接
- 电话服务
- 数据管理
- 后台任务(Background Task)管理
- 设备管理
- 设备使用信息统计
- DFX
- 国际化开发
- 折叠屏系列
- ……

更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册》
相关文章:
HarmonyOS实战开发-如何实现一个支持加减乘除混合运算的计算器。
介绍 本篇Codelab基于基础组件、容器组件,实现一个支持加减乘除混合运算的计算器。 说明: 由于数字都是双精度浮点数,在计算机中是二进制存储数据的,因此小数和非安全整数(超过整数的安全范围[-Math.pow(2, 53)&#…...
每日OJ题_子序列dp⑥_力扣873. 最长的斐波那契子序列的长度
目录 力扣873. 最长的斐波那契子序列的长度 解析代码 力扣873. 最长的斐波那契子序列的长度 873. 最长的斐波那契子序列的长度 难度 中等 如果序列 X_1, X_2, ..., X_n 满足下列条件,就说它是 斐波那契式 的: n > 3对于所有 i 2 < n&#x…...
病毒循环Viral Loop是什么?为何能实现指数增长
一、什么是病毒循环(Viral Loop)? 病毒循环(Viral Loop)是一种机制,它推动连续的推荐以实现持续增长。 它会促使你现有的客户推荐其他人,去认识你的品牌,然后让这些新客户进一步告诉…...
下载huggingface中数据集/模型(保存到本地指定路径)
一. snapshot_download # 1.安装huggingface_hub # pip install huggingface_hubimport osfrom huggingface_hub import snapshot_downloadprint(downloading entire files...) # 注意,这种方式仍然保存在cache_dir中 snapshot_download(repo_id"ibrahimhamam…...
HarmonyOS实战开发-使用List组件实现导航与内容联动的效果。
1 卡片介绍 使用ArkTS语言,实现一个导航与内容二级联动的效果。 2 标题 二级联动(ArkTS) 3 介绍 本篇Codelab是主要介绍了如何基于List组件实现一个导航和内容的二级联动效果。样例主要包含以下功能: 切换左侧导航ÿ…...
ArcGIS二次开发(一)——搭建开发环境以及第一个简单的ArcGIS Engine 程序
Arcgis10.2、Arcgis Engine10.2与Microsoft Visual Studio 2012的版本进行安装 1、推荐教程与安装包2、安装顺序3、安装成功测试VS新建项目可以创建ArcGIS项目,并且在VS中拖拽ArcGIS工具 4、搭建第一个简单的ArcGIS Engine 程序 ArcEngine和VS版本是有对应的&#x…...
Oracle 19c 高可用部署实战系列之Data Guard理论与实战
课程介绍 Oracle Data Guard确保企业数据的高可用性、数据保护和灾难恢复。 Oracle Data Guard提供了一组全面的服务,用于创建、维护、管理和监视一个或多个备用数据库,使生产Oracle数据库能够在灾难和数据损坏中幸存下来。Oracle Data Guard将这些备用…...
ubuntu常用记录
常用命令 ps aux |grep ... pip show pkgname nvidia-smi -l du -sh * df -h head -n 10 file.txt htop sudo apt install package_name kill process_id 软链接 在 Linux 中,软连接(Symbolic Link,也称为符号链接或软链接)是一…...
顺序表专题
文章目录 目录1. 数据结构相关概念1.1 什么是数据结构1.2 为什么需要数据结构 2. 顺序表的概念及结构3. 顺序表分类4. 实现动态顺序表4.1 初始化4.2 顺序表的尾部插入4.3 打印顺序表4.4 顺序表的头部插入4.5 顺序表的尾部删除4.6 顺序表的头部删除4.7 指定位置之前插入数据4.8 …...
手写SpringBoot(三)之自动配置
系列文章目录 手写SpringBoot(一)之简易版SpringBoot 手写SpringBoot(二)之动态切换Servlet容器 手写SpringBoot(三)之自动配置 手写SpringBoot(四)之bean动态加载 手写SpringBoot…...
vitepress builld报错
问题:build时报错:document/window is not defined。 背景:使用vitepress展示自定义的组件,之前build是没有问题了,由于新增了qr-code以及quill富文本组件,导致打包时报错。 原因:vitepress官…...
redis分布式锁-----基于Redis的SETNX命令的简单分布式锁实现
Redis的SETNX命令的简单分布式锁实现的Java示例 首先,确保你已经引入了Jedis这个Java Redis客户端库。你可以通过Maven或Gradle来添加依赖。 1、Maven依赖 <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifact…...
HTTP请求头中的Host表示是什么?
表示处理请求的服务器地址,由于一台服务器可能部署多个网站,如果通过域名访问,host就是域名...
apk被play protect blocked的解决方案(ADB+Appium+webdriverio)
起因:公司有海外项目,需要推广apk ,数量多,但是由于被play protect阻止安装,初版解决方案 apk加固、换签名、垃圾代码、修改资源文件的MD5,但是由于原生代码标记过于严重,推广成本高,又换了一种…...
【BlossomRPC】手把手教你写一个RPC协议
文章目录 新的开始什么是RPC?设计一个RPC需要些什么? 新的开始 经常会遇到一些项目,看着看着就发现看不懂文档了,也就是会出现一些跳过讲解的文章,使得自己很难了解某种中间件的开发全貌,所以想着自己先设计一个比较…...
算法之美:堆排序原理剖析及应用案例分解实现
这段时间持续更新关于“二叉树”的专栏文章,关心的小伙伴们对于二叉树的基本原理已经有了初步的了解。接下来,我将会更深入地探究二叉树的原理,并且展示如何将这些原理应用到更广泛的场景中去。文章将延续前面文章的风格,尽量精炼…...
Net8 ABP VNext完美集成FreeSql、SqlSugar,实现聚合根增删改查,完全去掉EFCore
没有基础的,请参考上一篇 彩蛋到最后一张图里找 参考链接 结果直接上图,没有任何业务代码 启动后,已经有了基本的CRUD功能,还扩展了批量删除,与动态查询 动态查询截图,支持分页,排序 实现原理…...
yolov8直接调用zed相机实现三维测距(python)
yolov8直接调用zed相机实现三维测距(python) 1. 相关配置2. 版本一2.1 相关代码2.2 实验结果 3. 版本二3.1 相关代码3.2 实验结果 相关链接 此项目直接调用zed相机实现三维测距,无需标定,相关内容如下: 1.yolov5直接调…...
element跑马灯/轮播图,第一页隐藏左边按钮,最后一页隐藏右边按钮(vue 开箱即用)
图示: 第一步: <el-carousel :class"changeIndex0?leftBtnNone:changeIndeximgDataList.length-1? rightBtnNone:" height"546px" :autoplay"false" change"changeNext"><el-carousel-item v-for…...
下载及安装PHP,composer,phpstudy,thinkPHP6.0框架
文章目录 目录 文章目录 前言 一、下载PHP 二、下载composer 三、下载PHPstudy 四、下载think PHP 1.下载 2.多应用开发 前言 thinkPHP是一款开源的PHP框架,它是基于MVC(Model-View-Controller)设计模式构建的。thinkPHP提供了丰富的…...
大量文件夹能一键改名吗?怎么改?4个干货技巧教你快速搞定
每次整理电脑文件时,面对成百上千个命名混乱的文件夹,手动逐个修改不仅耗时费力,还容易出现重复或格式错误。本文汇总了4种实用的批量重命名方法,从简单的系统自带功能到专业软件、插件工具,再到进阶的批处理脚本&…...
LVGL 7.11.0 Chart控件实战:5分钟搞定动态心率折线图(附完整代码)
LVGL 7.11.0 Chart控件实战:5分钟搞定动态心率折线图(附完整代码) 在嵌入式设备上实现流畅的数据可视化一直是开发者的痛点。LVGL作为轻量级图形库,其Chart控件能完美解决这一问题。本文将手把手教你用LVGL 7.11.0的Chart控件&am…...
从Word2Vec到BERT:聊聊Embedding技术这十年,我们踩过的‘坑’和收获的‘宝’
从Word2Vec到BERT:Embedding技术的十年演进与实战智慧 记得2013年第一次用Word2Vec处理电商评论时,我们团队对着"iPhone"和"安卓手机"的向量相似度兴奋不已——这两个在传统词袋模型里毫无关联的词,在向量空间中的余弦相…...
深入解析 Linux 内核中的 PCI 中断向量分配机制:pci_alloc_irq_vectors
1. PCI中断向量分配机制入门指南 第一次接触PCI设备中断处理时,我被各种专业术语搞得晕头转向。直到在项目里实际调试一个网卡驱动时,才真正理解pci_alloc_irq_vectors这个函数的重要性。想象一下,你的电脑就像个繁忙的快递分拣中心…...
【读书笔记】《逆风跑者》
《逆风跑者》| 长跑人的阿甘正传 如果你也曾困顿过,迷茫过,被生活压得喘不过气来,那么就拉过一把椅子静静地坐一会儿吧。听我说说这位无声跑者的事儿,和他一起不屈不挠地寂静奔跑一次。 📖 关于这本书 《逆风跑者》是…...
Windows Server远程管理新选择:一键脚本部署noVNC服务端(含开机自启配置)
Windows Server远程管理新选择:一键脚本部署noVNC服务端(含开机自启配置) 对于需要管理Windows Server的系统管理员来说,远程访问是不可或缺的功能。传统的RDP虽然稳定,但在某些场景下可能受限,比如网络环境…...
用快马AI快速原型设计:9·1免费素材库管理界面十分钟搭建指南
最近在帮朋友设计一个免费素材库的管理界面,需求是要快速搭建一个能展示"91免费素材"的网页应用。作为一个经常需要验证设计想法的开发者,我发现用InsCode(快马)平台可以大大缩短原型开发时间。下面分享下我是如何在十分钟内完成这个素材库管理…...
OpenClaw多模态探索:Qwen3-32B+RTX4090D镜像截图转报告实践
OpenClaw多模态探索:Qwen3-32BRTX4090D镜像截图转报告实践 1. 为什么选择这个技术组合 上周团队头脑风暴时,我遇到了一个典型痛点:会议室白板上写满了讨论要点,但拍照后整理成电子版纪要需要手动誊写半小时。作为技术负责人&…...
Zotero-GPT插件:如何正确配置API密钥以激活AI文献分析功能
Zotero-GPT插件:如何正确配置API密钥以激活AI文献分析功能 【免费下载链接】zotero-gpt GPT Meet Zotero. 项目地址: https://gitcode.com/gh_mirrors/zo/zotero-gpt Zotero-GPT是一款将GPT人工智能能力深度整合到Zotero文献管理软件中的开源插件,…...
从Python转C++必看:C++20的starts_with/ends_with和Python有何不同?5个易错点详解
从Python转C必看:C20的starts_with/ends_with和Python有何不同?5个易错点详解 当你在Python中熟练使用startswith()和endswith()多年后,突然切换到C20的starts_with和ends_with,可能会觉得"这不就是换个语法吗?&q…...
