ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

【口算王|01】HarmonyOS ArkTS 口算题生成实战:按年级、运算类型和难度生成可控题目

【口算王|01】HarmonyOS ArkTS 口算题生成实战:按年级、运算类型和难度生成可控题目 【口算王01】HarmonyOS ArkTS 口算题生成实战按年级、运算类型和难度生成可控题目口算应用看似只要随机两个数、拼一个运算符就能工作真正落地时却会迅速遇到边界问题一年级题目不能频繁出现负数除法不能产生无法口算的小数四则混合要遵守运算顺序错误选项不能重复随机练习不能改坏原题库所谓“难度”还必须能被代码和测试明确解释。本文基于口算王项目本地口算王工程脱敏路径的真实 ArkTS 源码重点复核MockBanks.ets、PracticePage.ets、QuestionUtils.ets与MathModels.ets。当前实现使用 ArkTS/ArkUI本轮没有依据构建配置或设备运行结果断言具体系统版本。题目完全由本地算法生成共有 6 个年级题库、7 类题型每个年级生成 250 条题目记录总计 1500 条记录数、唯一 ID 数和不同题干数需要分别统计。文章会区分“已经实现的行为”和“为了让难度真正可控而建议增加的约束”不会把设计方案写成现成功能。证据边界本文“当前实现”来自本轮对 MockBanks、PracticePage、QuestionUtils 与 MathModels 的静态复核并用独立脚本按相同公式重放了题型数量和内容签名统计。DifficultyRule、GenerationSpec、稳定内容 ID、校验结果类型和性质测试均为建议设计。本轮没有修改工程源码也没有执行 hvigor 构建、自动化测试、模拟器或真机验证。一、先定义“可控”再谈随机可控题目不是每次得到同一个结果而是无论随机种子如何变化都满足一组不变量题干合法 答案唯一 选项不重复 减法结果符合年级边界 除法保证整除 混合运算遵循明确规则 题型与筛选条件一致 题量不超过候选池 原始题库不被洗牌污染 相同题目 ID 对应稳定内容口算王当前生成器没有调用网络接口也没有在页面运行时临时向服务端请求题目。MockBanks.ets在模块加载时构造原始题目并缓存展开后的Question[]这让题目总量、ID 和解析都可以从源码复核。二、真实数据模型决定生成器能表达什么MathModels.ets中的题目接口是export interface Question { id: string bankId: string chapterId: string type: string stem: string audioHint?: string options: Option[] answer: string analysis: string example?: string }这里有bankId、chapterId和type但没有grade、difficulty、operandRange或skills字段。年级依赖bankId的命名约定推导难度则隐含在操作数范围和题型中。因此标题中的“按难度生成”必须谨慎解释当前代码实现的是“按年级间接改变数值范围并用题型表达部分认知复杂度”不是一个可由用户显式选择的easy / medium / hard难度系统。三、六个年级题库如何映射源码定义了 6 个年级{ id: grade1, name: 一年级, shortName: 一, ... } { id: grade2, name: 二年级, shortName: 二, ... } { id: grade3, name: 三年级, shortName: 三, ... } { id: grade4, name: 四年级, shortName: 四, ... } { id: grade5, name: 五年级, shortName: 五, ... } { id: grade6, name: 六年级, shortName: 六, ... }对应题库 ID 为b_grade1到b_grade6。生成时通过字符串替换得到年级const grade Number(bankId.replace(b_grade, ))这段代码简洁但它依赖命名格式。如果传入grade1、空字符串或未知 IDgrade可能成为NaN。当前getQuestions()在缓存没有命中时仍会调用buildQuestions(bankId)所以路由参数校验不能省略。四、250 题不是手写而是七类题型轮转每个题库目标数量为const BANK_TARGET_COUNT: number 250生成循环使用const slot (i - 1) % 7slot对应加、减、乘、除、混合、应用和限时七类题型。250 除以 7 的商是 35、余数是 5因此每个年级中前五类各生成 36 题应用题和限时题各生成 35 题。六个年级合计题型每年级六年级合计add36216sub36216mul36216div36216mixed36216word35210speed35210总计2501500这不是统计平台数据而是由确定性循环直接推导出的本地题库数量。五、年级目前怎样影响操作数范围加减题的两个基础操作数来自const a 1 ((i * 7 grade * 3) % (grade 1 ? 10 : grade 2 ? 60 : 120)) const b 1 ((i * 5 grade * 11) % (grade 1 ? 10 : grade 2 ? 50 : 90))由此可见一年级a、b主要落在 1 至 10。二年级上限分别扩展到 60 和 50。三至六年级上限分别为 120 和 90。这里的“难度”主要表现为数值范围扩大。三至六年级虽然偏移量不同但模数相同并没有随年级持续扩大数值边界。源码中的章节名称包含小数、分数、百分数和比例但当前生成器仍只生成整数四则与简单文字题。这意味着不能仅根据章节文案宣称五、六年级已实现小数、分数和百分数出题。真实能力边界仍由buildQuestions()决定。六、减法为什么不会出现负数减法分支先排序两个操作数const big Math.max(a, b) const small Math.min(a, b) pushQuestion( raws, sub, ${big} - ${small} ?, big - small, 减法先保证被减数更大避免出现负数。, i )这个约束保证答案不小于 0适合基础口算。如果未来增加“有理数”或“负数”难度档不能直接复用这条规则而应把allowNegative变成显式约束。还应注意big small时会生成答案 0。这不算错误但测试用例必须包含零结果避免错误选项生成器把 0 替换掉或出现重复。七、除法为什么一定整除除法不是先随机被除数而是先生成除数和商const divisor 1 ((i grade) % 9) const quotient 1 ((i * 3 grade) % 9) pushQuestion( raws, div, ${divisor * quotient} ÷ ${divisor} ?, quotient, 除法题保证整除可以用乘法反推答案。, i )被除数由divisor * quotient反推因此满足divisor ! 0 dividend % divisor 0 answer quotient这是比“随机两个数后检查是否整除”更稳定的构造方式不需要重试也没有无限循环风险。对于基础口算先生成答案空间再反推题干通常更容易保证约束。八、乘法、混合运算与限时题的真实边界乘法使用 1 至 9 的因子const x 1 ((i grade) % 9) const y 1 ((i * 2 grade) % 9)这符合九九乘法范围但各年级都使用同一因子上限。混合运算则固定为${x} × ${y} ${z} ?解析明确要求先乘后加。限时题固定为${x} - ${y} ${grade} ?按从左到右计算。这里的“限时”是题型标签真正倒计时在PracticePage的exam模式中实现单个speed题本身没有独立计时字段。九、应用题目前只有一种数量关系应用题模板为书架上有 ${first} 本书又放上 ${more} 本现在共有多少本答案是first more解析是“原有数量 新增数量”。它可以验证文字题展示和数量提取但不能代表已经覆盖减法比较、倍数、平均分、单位换算或多步应用题。若要增加模板建议先定义WordProblemTemplate让模板携带题型、单位、约束和答案函数而不是在一个if分支中继续堆叠字符串。十、错误选项怎样避免重复当前错误选项从答案偏移得到const offsets: number[] [ 0, 1 (seed % 4), -1 - (seed % 3), 5 (seed % 6) ]每个候选值都会经过两层处理if (value 0) value Math.abs(value) 2 while (values.indexOf(value) 0) value 2第一层避免负数选项第二层持续加 2直到四个选项互不重复。随后把正确答案从位置 0 交换到seed % 4让正确选项位置在 A 至 D 之间轮转。这套方法保证“唯一”和“非负”但干扰项质量仍有提升空间。例如答案较小时负偏移会被镜像成较大的正数对于乘除题基于常见计算错误生成干扰项会更有教学价值。十一、题目 ID 为什么稳定展开阶段生成id: ${bankId}_q${i 1} chapterId: ${regionId}_c${(i % 6) 1}只要题目顺序不改变同一个bankId下的 ID 就稳定收藏、错题和笔记可以通过 ID 找回题目。但如果以后在 250 题列表中间插入新题后续 ID 会整体漂移旧数据可能指向不同题目。更稳妥的演进方式是由题型、操作数和模板版本生成稳定键例如b_grade2:add:27:16:v1现有代码尚未采用这种键因此扩充题库前要先评估历史收藏和错题数据迁移。十二、章节标题和生成规则目前没有真正绑定每个年级有 6 个章节章节标题很具体例如一年级有“10以内加法”“20以内进位”五年级有“小数加减”“分数入门”。然而当前章节归属只是chapterId: ${regionId}_c${(i % 6) 1}这会把七类题型按索引轮流分散到六章而不是根据章节主题筛选。于是“10以内加法”章节中可能出现除法、混合运算或应用题“小数加减”章节也仍是整数题。这不是页面问题而是生成模型缺少章节约束。文章必须明确这一点不能把 UI 上的章节名当作题库内容已匹配的证据。十三、运算类型筛选发生在练习页PracticePage.aboutToAppear()先获取题库再按questionType过滤this.questions getQuestions(params.bankId) if (params.questionType) { this.questions this.questions.filter( (question: Question) question.type params.questionType ) }如果题型不存在列表会保持为空因为后续兜底条件明确排除了params.questionType。这有利于暴露错误参数但页面必须提供清晰空状态否则用户只会看到没有题目的界面。过滤是对缓存数组的派生结果不会改写题库。随机模式随后再对候选集取样顺序正确选择年级题库 - 可选题型过滤 - 随机抽取 20 题十四、随机练习不会修改缓存QuestionUtils.shuffle()先复制const result [...arr]再执行 Fisher-Yates 洗牌。因为洗牌发生在副本上QUESTIONS_CACHE中的题目顺序不会被一次练习永久改变。随机模式最多取 20 题const take Math.min(count, pool.length) return QuestionUtils.shuffle(pool).slice(0, take)当候选池不足 20 时返回全部候选题候选池为空时返回空数组。指定startQuestionId时找到目标题就把它固定在第一位再从其余题目中洗牌补足。这些边界都可以用纯函数测试验证。十五、挑战模式不是完全随机挑战模式调用pickHourlyExamQuestions()。它以当前 UTC 小时和bankId哈希构造种子再用线性同余序列驱动 Fisher-Yates 洗牌。结果具有两个特点同一题库在同一小时内题序稳定便于用户重复进入时保持一致。小时变化后整体轮换提供新的挑战组合。这与普通随机模式的Math.random()不同。测试时不要把两者混为一谈随机模式验证集合约束与不污染原数组挑战模式还要验证同小时可复现和跨小时可变化。十六、把“难度”从隐式经验升级为显式约束当前代码没有难度字段。若要真正支持难度选择可以先引入纯数据配置interface DifficultyRule { id: string minOperand: number maxOperand: number allowCarry: boolean allowBorrow: boolean allowNegative: boolean divisionExactOnly: boolean maxSteps: number }再把年级、章节和题型映射到规则而不是在buildQuestions()中继续增加三元表达式。生成器接收GenerationSpecinterface GenerationSpec { grade: number chapterId: string type: string difficultyId: string count: number seed: number }这是改进建议不是当前源码已经存在的接口。十七、生成器适合放在纯逻辑层口算题生成可以拆成五层Spec年级、章节、题型、难度、数量和种子。Constraint操作数范围、进退位、整除、步数和结果边界。Generator生成题干、答案、解析与例式。Distractor生成唯一、合理的错误选项。Session过滤、抽样、计时、作答和持久化。当前MockBanks.ets同时承担目录、题目构造、选项生成、章节分配、缓存和计数同步。规模较小时可工作但难度系统扩展后纯函数边界能显著降低测试成本。十八、必须覆盖的性质测试生成算法最适合做性质测试而不是只断言某一道题所有题目恰好 4 个选项 所有选项文本唯一 answer 一定是 A/B/C/D answer 对应文本等于计算结果 sub 的答案 0 div 的除数 ! 0 且保证整除 每个题目 ID 唯一 每个题目 chapterId 属于当前年级 按 type 过滤后所有题型一致 随机抽样数量 20 shuffle 不修改输入数组 同小时挑战顺序稳定还要测试无效bankId。当前命名解析可能产生NaN如果路由把未知 ID 传入生成器虽然循环仍可能产出题目但内容边界失去年级含义。更安全的做法是先验证 ID 是否属于BANKS。十九、性能与内存要看真实规模模块加载时RAW_MAP会立即为 6 个题库分别构造 250 道原始题也就是 1500 个RawQuestion。syncCatalogCounts()随后调用getQuestions()又把所有原始题展开为完整Question并放入缓存。这意味着启动阶段会同时持有原始题与展开题。1500 题对当前应用通常不算大但如果扩展到小数、分数、单位换算和多模板应用题双份常驻数据会增加内存和启动成本。可选优化包括按题库懒生成、只缓存展开题、把目录计数预计算到常量或使用相同种子按需重建。优化前应先测量启动时间和内存不能仅凭代码行数判断性能问题。二十、当前实现可以确认什么基于真实源码可以确认题目由本地算法生成。共有 6 个年级题库每库 250 题总计 1500 题。有 7 类题型题型数量可由循环规则推导。减法不产生负答案。除法通过反推被除数保证整除。每题有 4 个互不重复的非负选项。正确答案位置随种子在 A 至 D 轮转。支持按题型过滤、随机 20 题和同小时稳定挑战。洗牌复制数组不会改写缓存顺序。不能声称已有独立难度选择器或difficulty字段。章节标题已经约束对应题型和数值范围。五、六年级已生成小数、分数、百分数或比例题。所有年级的难度都随年级连续递增。自动化测试、真机性能或教学效果已经验证通过。二十一、从现有生成器到可控题库的最小升级路线建议按以下顺序演进第一步为 bankId、chapterId、type 增加输入校验 第二步补齐现有生成器的性质测试 第三步把难度规则提取成 DifficultyRule 第四步让章节显式绑定允许题型和数值约束 第五步为题目建立不随数组位置变化的稳定 ID 第六步按需生成并测量启动时间与内存 第七步在 HarmonyOS 手机、平板和 2in1 上验证练习流程口算题生成的关键不在于“随机得更像随机”而在于把教学边界变成代码能够执行、测试能够证明的约束。口算王当前实现已经具备本地生成、整除控制、非负减法、唯一选项和安全洗牌等基础下一步真正影响内容质量的是让章节与难度从界面文案进入数据模型。只有这样“按年级、运算类型和难度生成”才会从宣传描述变成可复核的工程能力。二十二、1500 条记录不等于 1500 种题干buildQuestions()的循环次数决定记录数expandQuestions()又按数组位置生成唯一 ID因此 6 个题库确实得到 1500 条记录和 1500 个不同 ID。但公式使用有限模数索引继续增长后会回到已经出现过的操作数组合。工程验收若只检查数组长度和 ID 唯一性就会漏掉内容重复。本轮按源码的同一组公式重放 250 次并用type stem answer作为内容签名。每个年级的统计如下年级记录数不同内容签名说明一年级250129加减模数较小重复最明显二年级250181加减范围扩大三年级250179乘除和应用题仍周期重复四年级250178数值上限未继续扩大五年级250180尚未生成小数或分数六年级250181章节名称未改变生成公式这个结果不是说重复记录必然没有训练价值相同题干可以服务于复习频次。但产品若把“题量”理解为内容广度就应单独展示去重后的题干数、模板数和技能点覆盖数不能用记录条数替代。二十三、题型数量均衡也不代表内容分布均衡250 对 7 取模后单库中加、减、乘、除、混合各 36 条应用和限时各 35 条六库汇总分别是 216、216、216、216、216、210、210。记录数量看起来十分均匀但乘法和除法每个年级都只有 9 种不同的题干与答案组合应用题也只有 20 种数字组合和一个固定模板。数量均衡每类记录条数接近 内容均衡不同题干、答案区间和易错点分布合理 教学均衡进位、退位、整除、估算等技能覆盖符合目标三者需要不同指标。当前轮转规则只解决第一层不能推出后两层。真正的难度审计还应统计答案分布、进退位比例、同题重复间隔、模板覆盖和相邻题相似度。二十四、建立三本账记录、身份和内容建议为题库构建报告同时维护三种计数避免“ID 唯一”掩盖“题干重复”。以下接口是建议设计并未出现在当前工程interface QuestionCountReport { recordCount: number uniqueIdCount: number uniqueContentCount: number duplicateContentCount: number } function contentSignature(question: Question): string { return [question.type, question.stem, question.answer].join(|) }recordCount用于验证生成器没有少产uniqueIdCount保护收藏和错题引用uniqueContentCount衡量内容广度。若重复是有意复习还应记录templateId与skillId把“同一知识点复现”和“无意的公式周期碰撞”区分开。二十五、输入校验应先于生成和缓存当前getQuestions()在缓存未命中时会执行RAW_MAP.get(bankId) || buildQuestions(bankId)。未知 ID 可能让年级解析成NaN随后仍生成 250 条失去年级语义的记录并被放进缓存。相比默默兜底边界层更适合返回可识别的失败结果interface QuestionLoadResult { success: boolean questions: Question[] message: string } function loadQuestions(bankId: string): QuestionLoadResult { const bank BANKS.find(item item.id bankId) if (!bank) { return { success: false, questions: [], message: 未知题库 } } return { success: true, questions: getQuestions(bank.id), message: } }页面收到失败后进入明确的错误或空状态不应再用另一次生成兜底掩盖路由参数问题。这样缓存键始终来自合法目录统计也不会混入无法解释的题库。二十六、可复现生成需要规则版本不只需要随机种子普通练习用Math.random()洗牌挑战模式则以 UTC 小时和题库哈希产生可复现顺序。这里的种子只控制“从现有数组怎样排列”并不固定题目内容。若以后修改操作数公式、题型轮转或模板即使种子相同抽到的业务内容仍会变化。需要长期复现时生成规格应同时携带ruleVersion、templateVersion和seed。规则版本决定候选集模板版本决定题干语义种子才决定候选集中的抽样顺序。历史考试记录保存这三个值后才能解释“为什么当时出现这道题”。二十七、把性质断言放在缓存写入之前生成结果进入QUESTIONS_CACHE后会被搜索、分类、收藏、错题和考试多处消费。越晚发现坏题影响面越大。建议构建阶段先校验整批结果再把通过的不可变快照写入缓存。assert recordCount spec.count assert every id is unique assert every question has exactly 4 unique options assert answer key exists in options assert option(answer) equals calculated result assert subtraction result is non-negative assert division is exact and divisor is not zero assert chapterId belongs to selected bank assert type belongs to allowed types assert uniqueContentRatio meets the product threshold最后一条阈值必须由产品和教学目标共同确定而不是随意要求 100%。例如复习模式允许有计划地重复但应保证重复间隔、错因覆盖和技能分布符合设计新题模式则可以要求更高的内容唯一率。二十八、验证顺序从纯函数走向页面最小验证链可以分四层执行。第一层测试操作数和答案纯函数第二层批量生成每个年级并输出三本账第三层验证题型过滤、章节过滤、普通随机与同小时挑战第四层才在 HarmonyOS 页面检查空状态、20 题上限、答题卡、收藏和错题回放。层级重点输入关键证据规则边界数、非法 ID明确成功或失败结果批量生成6 年级 × 7 题型数量、唯一性、分布报告会话抽样题型、章节、小时种子不污染缓存且数量不越界页面空池、长题干、重复进入状态可见、导航正确、数据可追踪本轮只完成静态复核和公式重放没有运行这些工程测试。把验证边界写清楚比用“理论上没问题”代替测试更可靠。二十九、总结先约束生成再度量内容口算王当前生成器已经具备非负减法、整除除法、四个唯一选项、按题型过滤、安全洗牌和同小时稳定抽样等基础能力。真正需要继续完善的不是简单增加循环次数而是把年级、章节、难度和内容多样性变成显式规格。工程上应同时守住三条线输入必须合法生成结果必须满足性质题量统计必须区分记录、身份和内容。这样“250 题”才不只是数组长度“按难度”也不只是数值上限而是能够被配置、测试、回放和解释的教学约束。AI 辅助声明本文在人工核对真实工程源码、题型分布、操作数规则、章节分配、缓存和抽样路径后使用 AI 辅助整理结构、润色表达并生成示意图当前实现、公式重放结果、建议扩展与未执行验证均已分别说明。CSDN-SERIES:ALL-163186049
返回列表