news 2026/10/10 9:52:02

手写Android科学计算器:调度场算法实现表达式解析全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手写Android科学计算器:调度场算法实现表达式解析全解析

1. 项目盘点:这个科学计算器到底做了什么

先说我翻出来的这个项目。这是一个基于Android Studio开发的科学计算器软件源代码,语言用的是Java,工程结构完整,核心计算逻辑没有引入任何第三方库,完全是自己写的表达式解析引擎。它覆盖的功能不只有加减乘除,三角函数、对数、开平方、幂运算、阶乘、取余、正负号切换这些科学计算里常用的操作都在里面,用户直接输入“sin(30)+sqrt(16)”这种自然表达式,按一下等号就能得到结果。

我为什么专门把它拎出来写一篇博文,是因为这类项目在网上的资源其实非常多,但质量参差不齐。大多数教程和源码停留在“能跑”的层面:界面按钮歪歪扭扭、按压没有反馈、表达式解析逻辑一塌糊涂,甚至有人直接调用系统自带的“eval”,遇到括号和负号就崩。真正把从工程搭建、界面布局到算法实现全链路讲清楚的很少。所以这篇内容我会按一个可复现的完整项目来拆,适合三类人看:正在做Android课程设计的学生、刚入门想练手的小白、以及想把这类计算器项目拿过去移植改造再做二次开发的开发者。

这个项目的亮点有三个。第一,表达式解析完全手写,基于调度场算法,也就是把中缀表达式转成后缀表达式再求值,这是数据结构课上的经典内容,写在课程设计里非常加分。第二,界面用GridLayout加权重方案来做,不管手机屏幕是宽是窄,按键都能均匀撑开,不会出现小屏挤成一团、大屏空一大片的尴尬。第三,所有按键共用一套样式和按压反馈资源,想改主题皮肤只需要动一个文件。

1.1 功能清单与UI整体观感

整个计算器界面分为两个区域,上方是显示区,下方是按键区。按键加上显示部分一共做了26个控制元素,我把功能列成一个表,方便大家对照着看:

分类按键说明
基础运算+ - * / %双目运算符,%是取余
幂运算^支持2^10这种写法
括号( )支持多层嵌套
三角函数sin cos tan参数按角度制输入,内部转弧度
反三角函数asin acos atan反三角结果按角度制输出
对数ln log自然对数与常用对数
其他函数sqrt 1/x x!开平方、倒数、阶乘
控制键C Del =清空、退格、计算结果
辅助键. +/-小数点、正负号切换

显示区我特意设计成两行。上一行实时显示当前输入的完整表达式,下一行展示上一次计算的结果。这样做的好处是用户能随时确认自己输入了什么,而不是只有一个孤零零的数字,出错时也容易定位。

1.2 工程结构与技术栈

工程的目录结构大概长这样:

ScientificCalculator/ ├── app/ │ ├── src/main/ │ │ ├── java/com/example/calculator/ │ │ │ ├── MainActivity.java // 界面交互入口 │ │ │ ├── CalculatorEngine.java // 表达式解析与计算核心 │ │ │ └── ButtonClickHandler.java // 按钮事件分发 │ │ └── res/ │ │ ├── layout/activity_main.xml │ │ ├── values/strings.xml │ │ ├── values/colors.xml │ │ ├── values/styles.xml │ │ └── drawable/ │ │ ├── bg_btn_normal.xml │ │ └── bg_btn_pressed.xml │ └── build.gradle └── build.gradle

技术栈非常朴素:Java 8语法级别,UI用XML声明式布局,计算引擎纯Java实现。minSdkVersion设为21,也就是Android 5.0,targetSdkVersion我用的33,这个范围基本覆盖市面上主流机型,又不太需要处理老版本兼容问题。整个项目除了Android自带的SDK,没有任何第三方依赖。

2. 环境准备:从下载Android Studio到跑通第一个工程

这个项目不挑Android Studio版本,但很多人一上来就卡在环境上。光下载安装折腾半天、项目导入了却编译不过,这些我都经历过,所以把环境这一环单独讲透。

2.1 Android Studio下载与国内镜像加速

下载Android Studio,第一选择永远是官网,地址是Android开发者官方网站里的Downloads页面,那里是最新最干净的安装包。不过国内网络环境下直接从官网拖这个安装包确实慢,几百MB的东西经常要挂很久。这种问题不用硬扛,国内几个大厂都有官方镜像,腾讯、华为、阿里都提供Android Studio安装包和SDK组件的镜像源,速度比直连官网快很多。我的个人习惯是:小版本更新就直接从官网下,大版本或者要补SDK组件的时候用镜像源拉,实测下来省时间也稳定。

另外注意一个老生常谈的点:新版Android Studio基本都自带了JetBrains Runtime,不需要额外安装JDK。但如果是老工程、老版本Gradle,可能会提示找不到JDK,这种情况去File > Project Structure里把SDK Location和JDK路径指正确就行。

网上还能搜到不少“android studio 3.5版本下载”的诉求,一般是老电脑跑不动新版。3.5时代的构建体系和插件都比较旧,拿来看老项目可以,新写工程就别折腾了,直接上最新稳定版,少踩兼容性的坑。

2.2 首次启动与中文界面设置

新版Android Studio默认是全英文界面,想改成中文有两个办法。最省事的是在Settings > Plugins里直接搜索“Chinese (Simplified) Language Pack”,这是JetBrains官方的中文语言包插件,装完之后重启就变成全中文界面。如果搜索不到,多半是插件仓库源的问题,可以手动去插件官网下载对应的zip包,再通过设置页齿轮图标里的“从磁盘安装插件”导入。

这里我想多说一句:设置中文确实降低上手门槛,但我建议英文菜单的大概位置也记一下,因为以后在网上搜教程、查报错信息,绝大多数资料和截图都是英文界面的,关键词也是英文的,比如Clean Project、Sync Project with Gradle Files。中英文切换壁垒其实是习惯问题,两边都认识,遇到问题才能更从容。

2.3 新建项目的关键参数

环境装好后开始实操。打开Android Studio,选择New Project,模板一定要选Empty Views Activity,注意不是Empty Activity,因为部分新版本里Empty Activity默认生成的是Compose模板,和这个Java加XML的项目对不上。关键参数这么填:

  • Name:ScientificCalculator
  • Package name:com.example.scientificcalculator
  • Language:选Java
  • Minimum SDK:API 21
  • Build configuration:Groovy DSL

把这些记好,不要在中文界面里手滑选了Kotlin或者Compose模板,否则后面拿到的源码还得重新适配工程配置,平白多出一堆事。

3. 界面实现:布局、样式与可复用的自定义按键

计算器的UI看起来简单,但真做起来有几个细节特别重要:按键平均分布、按压要有反馈、适配不同屏幕尺寸、代码不能冗余。这一节我把方案和踩坑点都过一遍。

3.1 整体布局:为什么选GridLayout而不是TableLayout

计算器的按键天然是网格结构,最直接的想法是用TableLayout或者GridLayout。我最终选GridLayout,核心原因是它能用layout_columnWeight和layout_rowWeight让按键按比例均分,而TableLayout对行内拉伸支持不好,每个按钮都要单独处理layout_weight,26个按钮写下来全是重复劳动。

具体布局是这样的:最外层是垂直LinearLayout,上半部分放显示区的TextView,下半部分放GridLayout,columnCount设为5、rowCount设为6。每个按键通过权重属性在水平方向各占五分之一、垂直方向各占六分之一,这样不管屏幕宽度是320dp还是420dp,按键都能均匀撑满整块区域,天然适配不同机型。

3.2 按键的按压反馈与圆角样式

很多课设计算器的按钮用的是系统默认样式,灰不溜秋,按下去没有任何视觉变化,显得很粗糙。我给这个项目做了两个drawable资源,一个是普通状态的浅蓝底圆角按钮,一个是按压状态的颜色加深,然后在styles.xml里定义一个CalcButton样式,把背景指向一个selector选择器:

<style name="CalcButton"> <item name="android:layout_width">0dp</item> <item name="android:layout_height">0dp</item> <item name="android:layout_columnWeight">1</item> <item name="android:layout_rowWeight">1</item> <item name="android:layout_margin">4dp</item> <item name="android:background">@drawable/bg_btn_selector</item> <item name="android:textSize">18sp</item> <item name="android:textColor">#FFFFFF</item> </style>

bg_btn_selector.xml里用selector包裹两个shape:

<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <shape> <solid android:color="#1E88E5"/> <corners android:radius="8dp"/> </shape> </item> <item> <shape> <solid android:color="#0D47A1"/> <corners android:radius="8dp"/> </shape> </item> </selector>

这个方案的好处是:当用户手指按下按钮时,系统根据state_pressed自动切换背景,松开时自动恢复,完全不用写任何触摸监听代码。成本极低,但手感提升非常明显,比默认按钮强太多了。

3.3 用style批量生成按键,减少XML冗余

26个控件如果每个都写一遍layout_width、textSize、layout_margin这些属性,activity_main.xml会膨胀成几百行的重复代码。正确的做法是把公共属性提取成style,就像上面的CalcButton,已经包含了大小、权重、背景、字号、颜色,每个按钮只需要声明自己的id、text和style:

<Button android:id="@+id/btnSin" style="@style/CalcButton" android:text="sin" />

以后要调整按键大小、圆角、配色,只需要改CalcButton一处,所有按键全局生效。这就是“自定义组件”思维在XML层面的落地。如果还想更进一步,可以写一个自定义View类继承Button,在构造函数里统一做初始化,但做计算器这种规模的项目,style方案已经足够,没必要把类层次搞得太复杂。

4. 计算引擎:表达式解析的完整实现

界面只是皮,计算核心才是这个项目的灵魂,也是最大的坎。很多人在这一步直接抄库,但其实手写一个表达式解析器比想象中简单,而且能学到很多东西。

4.1 自己手写解析器,还是直接引第三方库?

网上很多算法器项目直接引入exp4j、mXparser这类表达式解析库,一行代码完成字符串求值。如果你只想快速出功能、不关心原理,用库确实是最快路径。但科学计算器几乎是计算机专业学生必做的题目,用库等于把最该练的算法部分外包了。

我选择手写,理由有三个。一是调度场算法是数据结构栈的经典应用,写一遍对栈和递归的理解会深很多。二是没有任何第三方依赖,APK体积小,项目干净。三是不受库的API限制,想加什么样的函数规则都可以自己说了算。实际上完整实现也就200行左右的代码,性价比极高。

4.2 词法分析:把字符串切分成Token

计算的第一步,是把用户输入的字符串“2+sin(30)*3”逐字扫描,拆成一个一个的Token。Token的类型分四种:数字、运算符、函数名、括号。

切分逻辑核心是:数字字符和小数点累积成完整数字串;英文字母累积成函数名;其它单个符号直接作为一个Token。需要注意函数名可能是多位的,比如sin、cos、sqrt,所以要用while循环把连续字母都读进来,不能只取一个字符:

private static List<String> tokenize(String expr) { List<String> tokens = new ArrayList<>(); StringBuilder num = new StringBuilder(); for (int i = 0; i < expr.length(); i++) { char c = expr.charAt(i); if (Character.isDigit(c) || c == '.') { num.append(c); } else { if (num.length() > 0) { tokens.add(num.toString()); num.setLength(0); } if (Character.isLetter(c)) { StringBuilder fn = new StringBuilder(); while (i < expr.length() && Character.isLetter(expr.charAt(i))) { fn.append(expr.charAt(i)); i++; } i--; tokens.add(fn.toString()); } else { tokens.add(String.valueOf(c)); } } } if (num.length() > 0) { tokens.add(num.toString()); } return tokens; }

这段代码有个容易踩坑的地方:函数名读取的时候,内部while循环会把i推到字母末尾,所以外层for循环里要执行一次i--,否则会跳过字符。写完建议单步调试走一遍,特别是有连续函数嵌套的情况。

另外一个绕不开的问题就是负号。用户输入“-5+3”或者“2*(-3)”时,减号既可以是双目运算符也可以是负号。我的处理方式是在转后缀阶段判断:如果减号出现的位置应该是一个操作数,就把它当成负号处理,实质是往输出里加一个“-1”,再往运算符栈里压一个“*”,把负号统一转成“乘以-1”。

4.3 调度场算法:中缀表达式转后缀表达式

中缀表达式是给人看的,后缀表达式是给计算机算的。转换过程用到两个数据结构:一个输出列表保存后缀结果,一个操作符栈临时存放运算符。规则也不复杂:

  • 数字直接进输出列表;
  • 函数名和左括号压栈;
  • 右括号出现时,把栈里直到左括号的元素全部弹出到输出列表,左括号丢弃,如果左括号上面还有函数名,也一并弹出;
  • 新运算符的优先级小于等于栈顶优先级时,先把栈顶弹出到输出,再把自己压栈;
  • 表达式扫描完之后,把栈里剩余元素全部弹出。

我用“2+34”画了一张表帮助理解,数字2直接输出,运算符“”优先级高于“+”,先压栈,遇到数字4后结束,最后把栈里的“”和“+”依次弹出,最终得到后缀表达式“2 3 4 * +”。计算时先算34得到12,再算2+12得到14,完全符合人脑的优先级逻辑。

实际实现时还要考虑未匹配括号和负号。完整代码如下:

private static List<String> toPostfix(List<String> tokens) { List<String> output = new ArrayList<>(); Deque<String> stack = new ArrayDeque<>(); boolean expectOperand = true; for (String token : tokens) { if (isNumber(token)) { output.add(token); expectOperand = false; } else if (isFunction(token)) { stack.push(token); expectOperand = true; } else if (token.equals("(")) { stack.push(token); expectOperand = true; } else if (token.equals(")")) { while (!stack.isEmpty() && !stack.peek().equals("(")) { output.add(stack.pop()); } if (stack.isEmpty()) { throw new IllegalArgumentException("括号不匹配"); } stack.pop(); if (!stack.isEmpty() && isFunction(stack.peek())) { output.add(stack.pop()); } expectOperand = false; } else { if (token.equals("-") && expectOperand) { output.add("-1"); stack.push("*"); continue; } while (!stack.isEmpty() && isOperator(stack.peek()) && getPriority(stack.peek()) >= getPriority(token)) { output.add(stack.pop()); } stack.push(token); expectOperand = true; } } while (!stack.isEmpty()) { output.add(stack.pop()); } return output; }

重点解释expectOperand这个标记。它表示当前正在期待一个操作数还是期待一个运算符。初始是true,遇到数字、右括号之后变成false,遇到左括号、函数名、运算符之后变成true。这样在判断负号时,只需要看expectOperand是否为true,就能判定当前位置是不是需要负号,比看上一个字符要准确得多。

4.4 后缀表达式求值与科学函数映射

后缀表达式求值就非常简单了:准备好一个数字栈,从左到右扫描,遇到数字压栈,遇到运算符或函数就从栈里弹出对应数量的操作数,算完把结果再压回去,最后栈里剩下的唯一元素就是结果。

private static double evalPostfix(List<String> postfix) { Deque<Double> stack = new ArrayDeque<>(); for (String token : postfix) { if (isNumber(token)) { stack.push(Double.parseDouble(token)); } else if (isFunction(token)) { double arg = stack.pop(); switch (token) { case "sin": stack.push(Math.sin(Math.toRadians(arg))); break; case "cos": stack.push(Math.cos(Math.toRadians(arg))); break; case "tan": stack.push(Math.tan(Math.toRadians(arg))); break; case "ln": stack.push(Math.log(arg)); break; case "log": stack.push(Math.log10(arg)); break; case "sqrt": stack.push(Math.sqrt(arg)); break; default: throw new IllegalArgumentException("未知函数: " + token); } } else { double b = stack.pop(); double a = stack.pop(); switch (token) { case "+": stack.push(a + b); break; case "-": stack.push(a - b); break; case "*": stack.push(a * b); break; case "/": stack.push(a / b); break; case "^": stack.push(Math.pow(a, b)); break; case "%": stack.push(a % b); break; default: throw new IllegalArgumentException("未知运算符: " + token); } } } return stack.pop(); }

三角函数这里有个特别容易踩的坑:Math.sin接收的是弧度制参数,而用户习惯输入角度制。我这版统一做了角度转弧度,所以输入sin(30)得到0.5,符合直觉。如果你更想做成纯弧度制,把Math.toRadians那一层去掉即可,但一定要在界面上标清楚,否则用户会以为程序算错了。

阶乘我单独说明一下。“x!”按钮点击后直接在表达式末尾追加!,词法分析时把!当成一个独立Token,转后缀阶段遇到!直接输出到后缀序列,因为它是单目后缀运算符,天然跟在操作数后面。求值的时候,如果碰到!,就弹出栈顶数字做阶乘再压回去。这里建议限制输入范围,阶乘超过20以后double的精度已经跟不上了。

5. Activity交互与代码组织

引擎写完,下一步是把它和界面接起来。这个环节代码量不大,但特别考验组织能力,处理不好MainActivity会膨胀成一个大杂烩。

5.1 按钮点击的统一分发

26个按钮如果每个都单独写一个OnClickListener,代码会变成一片重复模板。我的做法是把数字键和功能键分开处理。功能键每个逻辑不同,比如C、Del、=,在onClick里用switch分支处理。数字键和运算符键逻辑相似,直接用循环批量设置监听器。

这里有个省事的技巧:XML里给数字键统一命名成btn0到btn9,代码中用getIdentifier批量查找:

for (int i = 0; i <= 9; i++) { int resId = getResources().getIdentifier("btn" + i, "id", getPackageName()); Button btn = findViewById(resId); btn.setOnClickListener(this); }

这样10个数字键只用了三行代码就全部绑定完毕,省去了写10个findViewById的麻烦。getIdentifier会有极小的汇总查找开销,但点击事件这种低频操作完全无所谓。不过要注意,这个方法依赖资源命名规律,如果项目里资源命名不规整,还是老老实实手写findViewById更稳。

5.2 表达式合法性校验与异常兜底

用户输入不可能是完全合法的,比如空表达式直接按等号、表达式里出现连续两个运算符、括号不匹配。这些情况如果直接交给引擎处理,很可能抛异常闪退。我做了三层防御:

第一层,输入阶段拦截。等号按键只有在当前表达式非空、最后一个字符不是运算符时才允许触发计算。第二层,引擎抛异常。CalculatorEngine里对不合法Token抛IllegalArgumentException,并带出错描述。第三层,Activity层catch。计算入口用try-catch包住,捕获异常后在结果显示行显示“表达式错误”,程序不崩溃。

三层加起来,用户几乎不可能把程序弄崩溃。这在课程设计答辩或者作品展示时,是非常加分的稳定性表现。

5.3 用好抽取方法快捷键整理代码

项目写大了MainActivity很容易膨胀,建议养成“抽取方法”的习惯。Android Studio的抽取方法快捷键是Ctrl+Alt+M,Windows系统是Ctrl+Alt+M,Mac是Cmd+Option+M。用法很简单:选中一段代码,按下快捷键,输入新的方法名,IDE会自动把这段代码搬到独立方法里,并把原来的位置替换成方法调用。

我在整理这个项目时,把“更新表达式显示”“计算结果”“处理退格”“处理清空”四个逻辑块分别抽成了独立方法,onClick主体从80行瘦身到30行,可读性好非常多。这个方法平时写代码也用得上,尤其是从网上抄来一大段代码想整理归类时,比手动剪切粘贴快太多。

6. 问题排查与移植经验

写项目的过程就是不停踩坑的过程。下面这些问题都是实际遇到过的高频坑,整理成一个速查清单给大家。

6.1 资源重复错误

“资源重复错误”是Android Studio里非常经典的一个坑,报错信息通常长这样:Error: Attribute XXX has already been defined,或者Duplicate resources。出现原因基本都是同一个资源被定义了两遍。

最常见的场景有两个。一是从网上下载项目后,别人工程里已经有同名资源,你把整个res文件夹覆盖粘贴进来,没有先删旧的。二是多模块项目里两个module的同名资源合并冲突。解决分三步:

  1. 看Build窗口的报错信息,里面会带资源路径,顺着路径找到重复文件或同名定义;
  2. 删掉重复项只保留一份。如果是values目录下的同名item,比如两个styles.xml都定义了CalcButton,就把后出现的或者内容不全的那个删掉;
  3. 执行Build > Clean Project,再重新Build。

如果清理之后还是报同样的错,多半是增量编译缓存出问题了,直接File > Invalidate Caches / Restart,让IDE重建缓存和索引。这个操作不会丢任何代码,只是重建索引,可能要花几分钟,遇到诡异问题先试这个。

6.2 编译不过的常见原因

项目导入新环境后编译失败,最普遍的原因不是代码错误,而是Gradle版本不匹配。报错里出现“Gradle sync failed”或者“Minimum supported Gradle version is X.X”,基本就是版本对不上。

解决办法是打开gradle-wrapper.properties文件,调整distributionUrl里的Gradle版本号,再在build.gradle里把Android Gradle Plugin版本改成匹配的版本。经验规律是:新版Android Studio打开老项目,大概率会提示升级Gradle,直接点升级按钮就好;老版本AS打开新项目,就需要手动降级,相对麻烦。

我的建议是先把本机Android Studio升到最新稳定版,然后让AS自动去适配旧项目。这是最省力的路径,比自己手工去查版本对照表靠谱。

6.3 项目移植到其它电脑的正确姿势

把项目从一台电脑搬到另一台电脑,最忌讳把整个目录原封不动压缩打包传过去。因为本地配置文件、build目录、.gradle缓存这些带着本机路径和机器信息的东西,过去了只会产生一堆报错。

正确做法是只拷贝这些必要文件:

ScientificCalculator/ ├── app/ │ ├── src/ // 所有源码和资源 │ └── build.gradle // 模块构建配置 ├── build.gradle // 项目级构建配置 ├── settings.gradle // 工程设置 ├── gradle.properties // Gradle属性 └── gradle/ └── wrapper/ └── gradle-wrapper.properties

新电脑上打开项目后不要着急,等Gradle自动同步,如果提示SDK缺失,就去Project Structure里把SDK Location指到本机SDK路径。移植过来的第一件事永远是执行一次Build > Clean Project,把带过来的build残留清干净再编译。很多人新环境编译报各种奇怪的错,十有八九是旧的build目录惹的祸。

6.4 界面适配与数学符号显示

适配问题主要体现在两处。一是小屏手机上按键文字挤在一起,解决办法是把GridLayout的按钮间隔从4dp调到2dp,适当减小textSize,这些参数都集中在styles.xml里,改一处全局生效。二是部分机型中文字体显示偏粗,可以在显示区的TextView上显式设置fontFamily为sans-serif,让系统用默认无衬线字体渲染,整体会清爽很多。

还有一个隐藏比较深的坑:界面里要显示“×”“÷”“√”“π”这些数学符号,如果项目的源码字符编码不是UTF-8,编译时会出现乱码甚至编译失败。Android Studio新版本默认UTF-8基本不会踩到,但老项目如果是从GBK编码环境迁移过来的,记得在build.gradle里配置好源码编码,确保中文字符和数学符号都能正常读取显示。

7. 实际效果与我的最终建议

项目完整跑起来之后,我测试了一批典型输入:sin(30)输出0.5,sqrt(169)输出13,2^10输出1024,5!输出120,1/(2+3)输出0.2,多层括号嵌套也能正确计算,结果都符合预期。从环境搭建到完整跑通,熟练的话两三个小时可以搞定,新手花一个晚上也足够。

最后分享几点实际体会。第一,计算器项目最重要的不是界面多花哨,而是算法逻辑严谨。提交之前多测边界情况:连按等号、连续输入小数点、除以0、负数开平方根,这些场景都要有合理反应而不是崩溃。第二,代码注释要适度。类和方法级别的功能注释必须有,但没必要每行都注释一遍,那样反而显得很不专业。第三,如果想在此基础上继续扩展,优先级最高的方向是界面主题切换和计算历史记录。这两个功能实现成本低、演示效果好,放在作品集里非常加分。

还有一个实用建议,项目做完之后用Git做版本管理,第一次提交时记得排除.local.properties这类带本机路径的配置文件,以后换电脑、换版本、给别人参考都会非常从容。计算器虽小,但它把一个完整Android项目的环境配置、界面搭建、算法实现、异常处理、工程迁移全部走了一遍。认真做完,收获远比想象的多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 9:52:01

叙事性新闻游戏开发实战:优步司机经济困境的可交互系统设计

简介&#xff1a;这是一款由英国《金融时报》制作的叙事性新闻游戏源码&#xff0c;围绕优步司机的经济处境与零工经济体验展开&#xff0c;玩家需在一周内尝试赚取1000美元&#xff0c;并在真实司机访谈面前做出抉择。资源面向对数据新闻、交互叙事与前端开发感兴趣的开发者与…

作者头像 李华
网站建设 2026/10/10 9:50:29

数据结构1基础入门:掌握数据组织思维与复杂度分析,少走弯路

数据结构这门课&#xff0c;网上资料多到让人眼花缭乱&#xff0c;但真正能把“数据结构1”的基础打扎实的人&#xff0c;其实并不算多。很多人上来就啃《数据结构》教材&#xff0c;被C语言的指针和递归劝退&#xff0c;或者直接用Python刷题&#xff0c;刷到最后发现连复杂度…

作者头像 李华
网站建设 2026/10/10 9:49:59

PHP网约车H5系统源码:全链路生产级实现与高并发抢单设计

简介&#xff1a;这是一套基于Yii框架开发的PHP网约车H5系统源码&#xff0c;面向Web全栈开发者与PHP中级学习者&#xff0c;提供乘客端、司机端及后台管理三端一体化解决方案&#xff0c;适用于毕业设计、创业原型验证或本地化打车平台二次开发。资源包共2000个文件&#xff0…

作者头像 李华
网站建设 2026/10/10 9:49:27

阿里云ECS上用Docker部署HiClaw的完整指南

1. 部署前的整体思路与方案选型1.1 为什么选阿里云服务器跑HiClaw先说结论&#xff1a;如果你只是想快速验证HiClaw这套服务能不能跑通、要不要长期挂机运行&#xff0c;阿里云ECS是目前上手成本最低的选择之一。我前后在好几台机器上折腾过HiClaw的部署&#xff0c;包括本地虚…

作者头像 李华
网站建设 2026/10/10 9:49:25

Windows下OpenClaw部署实操:本地模型接入与Skill配置

最近我在自己的Windows主力机上折腾OpenClaw&#xff0c;前后断断续续弄了小半天&#xff0c;中间还翻了一次不小的车。这工具最近讨论度确实高&#xff0c;但多数教程要么停在“接云端API跑个Demo”&#xff0c;要么只讲Linux/macOS怎么操作&#xff0c;真正把Windows下从零装…

作者头像 李华
网站建设 2026/10/10 9:49:12

Java的注解:代码的元数据

378 Java的注解:代码的元数据 你可能已经写过@Override,但你知道它到底是什么吗?注解(Annotation)是Java 5引入的一种"元数据"——它是贴在代码上的"标签",告诉编译器或框架"这段代码有什么特殊含义"。 一、什么是注解? 注解就像代码的…

作者头像 李华