零基础鸿蒙应用开发第九节:流程控制综合练习
【学习目标】
- 吃透嵌套循环的核心逻辑(行/列控制),能独立实现杨辉三角、99乘法表的算法
- 掌握猜数字游戏的核心规则:随机数生成、输入校验、次数控制、分支判断、游戏终止逻辑
- 理解二维数组的初始化、赋值与遍历,解决杨辉三角这类结构化数据存储问题
- 掌握输入校验的三层过滤逻辑(空值、类型、范围),提升程序健壮性
- 掌握字符串格式化技巧(对齐、填充),优化控制台输出可读性
【学习重点】
- 嵌套循环:外层控“整体结构(行)”,内层控“局部细节(列)”
- 输入校验:
isNaN()判断数字合法性 + 范围边界判断,拦截非法输入 - 二维数组:杨辉三角的核心数据结构,重点掌握“上一行数据复用”生成当前行
- 分支判断:猜数字游戏的多状态(猜大/猜小/猜对/次数用尽)逻辑闭环
- 状态控制:通过布尔变量(如
isGameOver)控制流程启停,避免无效操作
一、工程结构
本节我们将创建名为ArkTSFunPractice的工程,基于 鸿蒙5.0(API12) 开发,使用 DevEco Studio 6.0+ 工具,项目结构目录如下:
ArkTSFunPractice/
├── AppScope # 应用全局配置
├── entry/
│ ├── src/
│ │ ├── main/
│ │ │ ├── ets/
│ │ │ │ ├── entryability/ # 应用入口
│ │ │ │ │ └── EntryAbility.ets
│ │ │ │ ├── pages/ # 页面代码
│ │ │ │ │ └── Index.ets # 核心页面(猜数字游戏完整代码)
│ │ │ │ └── utils/ # 纯逻辑工具函数
│ │ │ │ └── TableLogic.ets # 杨辉三角+99乘法表逻辑
│ │ │ ├── resources/ # 资源文件(默认生成,无需修改)
│ │ │ └── module.json5 # 模块配置(默认生成,无需修改)
二、案例解析
案例1:猜数字游戏
以下是猜数字游戏的纯逻辑代码,实现“随机数生成→输入校验→次数控制→分支判断→游戏终止”的完整规则闭环:
// 游戏核心状态
let targetNumber: number = generateRandomNumber(); // 目标数字(1-100)
let userInput: string = ''; // 用户输入的字符串
let guessCount: number = 0; // 已猜次数
let maxAttempts: number = 10; // 最大尝试次数
let message: string = '请输入1-100之间的数字开始游戏'; // 提示信息
let history: string[] = []; // 历史记录
let isGameOver: boolean = false; // 游戏是否结束
// 1. 生成1-100的随机数(核心算法)
function generateRandomNumber(): number {
return Math.floor(Math.random() * 100) + 1; // 关键:+1 避免生成0,保证范围1-100
}
// 2. 重置游戏(状态重置逻辑)
function resetGame() {
targetNumber = generateRandomNumber(); // 重新生成答案
userInput = ''; // 清空输入缓存
guessCount = 0; // 重置次数
message = '游戏已重置,请重新输入1-100之间的数字';
history = []; // 清空历史记录
isGameOver = false; // 激活游戏流程
}
// 3. 猜数字核心逻辑(核心规则实现)
function handleGuess() {
// 前置拦截:游戏结束后拒绝处理新猜测
if (isGameOver) return;
// 输入校验(三层过滤,核心健壮性保障)
const guess = Number(userInput);
if (isNaN(guess) || guess < 1 || guess > 100) {
message = '请输入有效的1-100之间的数字!';
console.log('[猜数字游戏]', message); // 替代UI提示,控制台输出
return;
}
// 次数计数:每提交一次猜测,次数+1
guessCount++;
const remaining = maxAttempts - guessCount; // 计算剩余次数
const record = `第${guessCount}次:${guess}`; // 构建历史记录模板
// 核心分支判断:根据猜测值与目标值的关系更新状态
if (guess > targetNumber) {
message = `猜大了!还剩${remaining}次机会`;
history.unshift(`${record} → 猜大了`);
} else if (guess < targetNumber) {
message = `猜小了!还剩${remaining}次机会`;
history.unshift(`${record} → 猜小了`);
} else {
// 猜对:终止游戏流程
message = `恭喜你猜对了!答案就是${guess},共猜了${guessCount}次`;
history.unshift(`${record} → 猜对了!🎉`);
isGameOver = true;
console.log('[猜数字游戏]', message);
return; // 猜对后直接退出,避免后续次数用尽判断
}
// 次数用尽:终止游戏流程
if (remaining === 0) {
message = `游戏结束!正确答案是${targetNumber}`;
history.unshift(`次数用尽 → 正确答案:${targetNumber}`);
isGameOver = true;
}
// 输出本次操作结果
console.log('[猜数字游戏]', message);
// 清空输入缓存,准备下一次输入
userInput = '';
}
// 测试示例(控制台运行,可选)
// userInput = "50"; handleGuess(); // 模拟输入50并提交
// userInput = "80"; handleGuess(); // 模拟输入80并提交
// resetGame(); // 重置游戏
关键逻辑要点
| 逻辑模块 | 核心作用 | 关键代码/规则 |
|---|---|---|
| 随机数生成 | 确定游戏唯一答案 | Math.floor(Math.random() * 100) + 1(+1是核心,保证1-100范围) |
| 输入校验 | 拦截非法输入,避免程序异常 | isNaN(guess)判断数字合法性 + `guess < 1 |
| 次数控制 | 限制游戏次数,控制流程节奏 | guessCount++计数 + remaining = maxAttempts - guessCount计算剩余次数 |
| 分支判断 | 实现游戏核心规则 | 猜大/猜小:更新提示+历史;猜对/次数用尽:设置isGameOver = true终止游戏 |
| 状态重置 | 支持游戏重新开始 | 所有变量恢复初始值,且重新生成随机数 |
⚠️ 易错点
- 随机数范围错误:漏写
+1,导致生成0-99的数字,不符合1-100的规则; - 未拦截游戏结束状态:
isGameOver为true时仍执行猜数字逻辑,导致次数异常增加; - 输入校验遗漏:未判断
isNaN(guess),用户输入非数字时会导致后续比较逻辑出错; - 猜对后未提前退出:猜对后仍执行后续的次数用尽判断,可能覆盖正确提示。
案例2:杨辉三角(嵌套循环+二维数组)
纯逻辑实现杨辉三角的生成与控制台格式化输出,核心是“二维数组存储+嵌套循环生成+边界规则+上一行数据复用”:
/**
* 杨辉三角生成逻辑
* @param rowCount 生成行数,默认10行
*/
export function testYanghuiTriangle(rowCount: number = 10) {
// 输入校验:限制行数范围,非法值兜底(健壮性设计)
if (isNaN(rowCount) || rowCount < 1 || rowCount > 20) {
rowCount = 10;
console.log("[testYanghuiTriangle] 输入行数非法,默认显示10行");
}
console.log(`\n===== 杨辉三角(${rowCount}行) =====`);
// 二维数组:存储杨辉三角的所有行数据
let triangle: number[][] = [];
// 外层循环:控制生成的总行数(行索引从0开始)
for (let rowIdx: number = 0; rowIdx < rowCount; rowIdx++) {
let currentRow: number[] = []; // 临时存储当前行的元素
// 内层循环:控制当前行的元素个数(列索引≤行索引,保证第n行有n+1个元素)
for (let colIdx: number = 0; colIdx <= rowIdx; colIdx++) {
// 边界规则:每行第一个/最后一个元素固定为1
if (colIdx === 0 || colIdx === rowIdx) {
currentRow.push(1);
} else {
// 核心算法:中间元素 = 上一行的当前列-1元素 + 上一行的当前列元素
const prevRow: number[] = triangle[rowIdx - 1];
currentRow.push(prevRow[colIdx - 1] + prevRow[colIdx]);
}
}
triangle.push(currentRow); // 将当前行存入二维数组
}
// 格式化输出(控制台可视化,非核心逻辑)
triangle.forEach((row: number[], index: number) => {
const spaceCount: number = (rowCount - index - 1) * 2;
const rowStr: string = row.join(' ');
// 由于空格无法被打印,用*占位
console.log('*'.repeat(spaceCount) + rowStr);
});
}
关键逻辑要点
| 逻辑模块 | 核心作用 | 关键代码/规则 |
|---|---|---|
| 输入校验 | 避免非法行数导致逻辑异常 | `isNaN(rowCount) |
| 二维数组初始化 | 存储结构化的杨辉三角数据 | triangle: number[][] = [] 定义二维数组,currentRow: number[] = [] 初始化当前行 |
| 外层循环 | 控制总行数 | rowIdx从0到rowCount-1,逐行生成 |
| 内层循环 | 控制每行元素数 | colIdx从0到rowIdx,保证第n行有n+1个元素 |
| 边界规则 | 杨辉三角基础规则 | `colIdx === 0 |
| 中间元素计算 | 杨辉三角核心算法 | prevRow[colIdx - 1] + prevRow[colIdx](复用上一行数据) |
⚠️ 易错点
- 二维数组行未初始化:直接给
triangle[rowIdx][colIdx]赋值,未先创建currentRow,导致数组越界; - 内层循环条件错误:写成
colIdx < rowCount,导致每行元素数超出(正确应为colIdx <= rowIdx); - 中间元素计算错误:误用当前行数据(
triangle[rowIdx][colIdx - 1])而非上一行(prevRow); - 行索引理解错误:忘记
rowIdx从0开始,导致行数少生成1行。
案例3:99乘法表(嵌套循环+格式化)
纯逻辑实现两种样式的99乘法表,核心是“嵌套循环控制行/列组合+字符串格式化保证输出对齐”:
/**
* 99乘法表生成
*/
export function testMultiplicationTable() {
console.log("\n===== 99乘法表(直角三角形版) =====");
for (let i: number = 1; i <= 9; i++) { // 外层:控制乘数(行)
let line: string = "";
// 内层:控制被乘数(列),仅到当前行,避免1×2和2×1重复
for (let j: number = 1; j <= i; j++) {
line += `${j}×${i}=${j*i}\t`; // \t制表符保证元素对齐
}
console.log(line);
}
}
⚠️ 易错点
- 内层循环条件错误:写成
j <= 9,导致生成完整矩形而非三角; - 格式化对齐遗漏:未用
\t或padEnd,导致输出内容参差不齐; - 循环变量混淆:将乘数和被乘数搞反(如
i×j写成j×i),不影响结果但不符合常规展示习惯;
三、完整代码示例
// UI内容本阶段不学习,重点掌握代码程序逻辑基础。
import { promptAction, router } from '@kit.ArkUI'
import { testYanghuiTriangle, testMultiplicationTable } from '../utils/TableLogic';
@Entry
@Component
struct Index {
// 游戏核心状态(@State 感知数据变化刷新UI)
@State targetNumber: number = this.generateRandomNumber() // 目标数字(1-100)
@State userInput: string = '' // 用户输入的字符串
@State guessCount: number = 0 // 已猜次数
@State maxAttempts: number = 10 // 最大尝试次数
@State message: string = '请输入1-100之间的数字开始游戏' // 提示信息
@State history: string[] = [] // 历史记录
@State isGameOver: boolean = false // 游戏是否结束
// 页面加载时执行杨辉三角和乘法表逻辑(控制台输出)
aboutToAppear() {
testYanghuiTriangle();
testMultiplicationTable();
}
// 生成1-100的随机数
generateRandomNumber(): number {
return Math.floor(Math.random() * 100) + 1;
}
// 重置游戏
resetGame() {
this.targetNumber = this.generateRandomNumber();
this.userInput = '';
this.guessCount = 0;
this.message = '游戏已重置,请重新输入1-100之间的数字';
this.history = [];
this.isGameOver = false;
}
// 处理猜数字逻辑
handleGuess() {
if (this.isGameOver) return;
const guess = Number(this.userInput);
if (isNaN(guess) || guess < 1 || guess > 100) {
this.message = '请输入有效的1-100之间的数字!';
promptAction.showToast({ message: this.message });
return;
}
this.guessCount++;
const remaining = this.maxAttempts - this.guessCount;
const record = `第${this.guessCount}次:${guess}`;
if (guess > this.targetNumber) {
this.message = `猜大了!还剩${remaining}次机会`;
this.history.unshift(`${record} → 猜大了`);
} else if (guess < this.targetNumber) {
this.message = `猜小了!还剩${remaining}次机会`;
this.history.unshift(`${record} → 猜小了`);
} else {
this.message = `恭喜你猜对了!答案就是${guess},共猜了${this.guessCount}次`;
this.history.unshift(`${record} → 猜对了!🎉`);
this.isGameOver = true;
return;
}
if (remaining === 0) {
this.message = `游戏结束!正确答案是${this.targetNumber}`;
this.history.unshift(`次数用尽 → 正确答案:${this.targetNumber}`);
this.isGameOver = true;
}
this.userInput = '';
}
build() {
Column() {
// 游戏标题
Text('猜数字小游戏')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin(20)
.onClick(()=>{
router.push({
url:"pages/nextPage"
})
})
// 游戏规则
Text(`规则:猜1-100之间的数字,共${this.maxAttempts}次机会`)
.fontSize(14)
.fontColor('#666')
.margin({ bottom: 20 })
// 输入区域
Row() {
TextInput({
placeholder: '请输入数字',
text: this.userInput
})
.type(InputType.Number) // 限制为数字输入
.width(200)
.height(40)
.border({ width: 1 })
.padding(5)
.onChange((value) => this.userInput = value)
Button('猜一下')
.width(100)
.height(40)
.margin({ left: 10 })
.onClick(() => this.handleGuess())
}
.margin({ bottom: 20 })
// 提示信息
Text(this.message)
.fontSize(16)
.fontColor(this.isGameOver ? '#e53935' : '#2e7d32')
.margin({ bottom: 10 })
// 历史记录
Text('历史记录:')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.margin({ bottom: 5, top: 10 })
Scroll() {
Column() {
ForEach(this.history, (item:string) => {
Text(item)
.fontSize(14)
.margin(2)
.fontColor('#555')
})
}
}
.height(200)
.width('100%')
.border({ width: 1, style: BorderStyle.Dashed })
.padding(5)
// 重置按钮
Button('重新开始')
.width(150)
.height(40)
.margin({ top: 20 })
.backgroundColor('#42a5f5')
.onClick(() => this.resetGame())
}
.width('100%')
.height('100%')
.padding(15)
.backgroundColor('#f5f5f5')
}
}
运行结果

四、内容总结
1. 逻辑核心回顾
- 猜数字游戏:核心是随机数生成+输入校验+次数控制+分支判断+状态终止,重点掌握“三层输入过滤”和“游戏状态闭环”;
- 杨辉三角:核心是二维数组+嵌套循环+边界规则+上一行数据复用,重点掌握“中间元素的计算逻辑”;
- 99乘法表:核心是嵌套循环的行/列控制,重点是
j <= i避免重复。
2. 通用逻辑技巧
- 输入校验优先:任何接收外部输入的逻辑,先做合法性校验,再处理核心规则;
- 嵌套循环分层:外层控“行/整体”,内层控“列/细节”,避免层级超过2层;
- 状态控制闭环:用布尔变量(如
isGameOver)控制流程启停,避免无效操作; - 数据结构匹配场景:结构化数据(如杨辉三角)用二维数组,线性数据(如历史记录)用一维数组。
五、代码仓库
- 工程名称:ArkTSFunPractice
- 仓库地址:https://gitee.com/juhetianxia321/harmony-os-code-base.git
六、下节预告
本节我们学习了猜数字小游戏、杨辉三角、九九乘法表的实现逻辑,对之前所学知识做了一次系统巩固,但还留下了关键的知识伏笔。在逻辑与运算章节中,我们发现 0.1 + 0.2 = 0.30000000000000004 而非预期的 0.3,当时仅知晓是浮点数精度问题,未深究底层原因。
下一节我们将进入一个重要的知识分支——计算机底层进制与编码原理,核心学习:
- 掌握二进制、八进制、十进制、十六进制的定义,以及在ArkTS中的表示方法与相互转换技巧;
- 理解位(bit)、字节(Byte)、半字节(Nibble)的核心概念,以及它们之间的换算关系;
- 彻底搞懂ASCII码的本质、表示形式,以及在ArkTS中的实际应用场景;
- 掌握二进制科学计数法的规范化表示,为后续学习浮点数存储标准(IEEE 754)打下基础;
- 建立“计算机所有数据均以二进制存储”的核心认知,理解整数、浮点数的存储本质,揭开“0.1 + 0.2 ≠ 0.3”的底层奥秘。
通过下一节的学习,你将打通计算机底层存储的基础逻辑,为后续深入理解数据类型、数值运算精度、编码转换等知识点提供关键支撑,让编程思维从“表面应用”深入到“底层原理”。
浙公网安备 33010602011771号