freeCodeCamp 每日编码挑战 Matrix Builder(Challenge 87)详解:JavaScript 零值矩阵生成与数组维度陷阱

发布时间:2026/9/10 14:21:30
freeCodeCamp 每日编码挑战 Matrix Builder(Challenge 87)详解:JavaScript 零值矩阵生成与数组维度陷阱 freeCodeCamp 每日编码挑战 Matrix BuilderChallenge 87详解JavaScript 零值矩阵生成与数组维度陷阱【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp本篇文章围绕 freeCodeCamp 开源仓库中每日编码挑战系列Daily Coding Challenges的第 87 题Matrix Builder展开完整还原题目描述、判定断言与参考实现并从仓库源码角度剖析它在课程体系中的定位challengeType: 28、从 Markdown 到图数据库再到数据库的流转链路以及rows × cols零矩阵生成中高频出现的数组行共享引用陷阱。读完本文你既能独立通过该题也能理解这套每日挑战是如何在 freeCodeCamp 前端与课程仓库中被组织、校验和投递的。题目档案Challenge 87 到底要求什么本挑战的源文件位于 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/68ee9e3066cfd4eb2328e8a6.md其 frontmatter 定义了这道题在仓库中的元信息--- id: 68ee9e3066cfd4eb2328e8a6 title: Challenge 87: Matrix Builder challengeType: 28 dashedName: challenge-87 ---任务描述给定两个整数——行数rows和列数cols——返回一个指定大小的零值矩阵matrix即数组的数组每个元素都为0。题目给出的示例是传入2和3即 2 行 3 列应返回[ [0, 0, 0], [0, 0, 0] ]这里需要先厘清行与列的对应关系外层数组的长度等于行数rows而每一个内层子数组代表一行的长度等于列数cols。也就是说2 和 3指的是 2 行、每行 3 个零而不是相反。挑战类型的语义28 dailyChallengeJsfrontmatter 中的challengeType: 28不是随意数字。在 packages/shared/src/config/challenge-types.ts 中可以看到明确映射const dailyChallengeJs 28; const dailyChallengePy 29;即类型28是 freeCodeCamp 专门为JavaScript 每日编码挑战预留的挑战类型29则对应 Python 版本。该配置还通过challengeTypes.dailyChallengeJs暴露给整个客户端与服务端共享包使用前端路由 client/src/client-only-routes/show-daily-coding-challenge.tsx 中即存在challengeType: 28的分支逻辑。判定条件拆解四条断言到底在测什么# --hints--区块是这套每日挑战的评分核心。Challenge 87 一共给出四条断言全部通过后题目才算通过assert.deepEqual(buildMatrix(2, 3), [[0, 0, 0], [0, 0, 0]]); assert.deepEqual(buildMatrix(3, 2), [[0, 0], [0, 0], [0, 0]]); assert.deepEqual(buildMatrix(4, 3), [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]); assert.deepEqual(buildMatrix(9, 1), [[0, 0, 0, 0, 0, 0, 0, 0, 0]]);原文中最后一条为 9 个独立内层数组即buildMatrix(9, 1)返回 9 行 × 1 列。把断言拆开看可以归纳出题目验证的四个维度调用行数列数结构要点buildMatrix(2, 3)23常规矩形2 行、每行 3 个零buildMatrix(3, 2)32行列互换对称性3 行、每行 2 个零验证参数方向不写反buildMatrix(4, 3)43更大的矩形验证循环次数正确buildMatrix(9, 1)91高而窄的极端形态9 行、每行仅 1 个零判定使用assert.deepEqual而非assert.equal或这一点至关重要deepEqual会递归比较两个数组的内部结构与元素值而不是比较引用地址。因此你的函数必须返回一个全新的嵌套数组结构且每一层、每个元素都与期望值逐项相等单纯返回一个形状像矩阵但内部不是独立数组行的对象是无法通过的。buildMatrix(3, 2)与buildMatrix(2, 3)两组用例故意把两个参数互换正是为了考察你是否把rows当作外层循环次数、把cols当作内层长度而不至于弄反维度。起始代码seed分析为什么初始实现必然失败# --seed--下的--seed-contents--给出了一个带有迷惑性的函数骨架function buildMatrix(rows, cols) { return rows; }它表面上完成了函数声明实则直接返回了第一个入参rows一个数字这会在所有四条assert.deepEqual中断言失败——因为返回的不是数组更不是嵌套数组。这种能跑但必然报错的起始代码是 freeCodeCamp 经典题目设计手法把解题者直接推向先读描述、再读断言、最后动手改的正确工作流而不是照着编译器的报错去试错。官方参考实现与数组引用陷阱挑战文件末尾的# --solutions--区块给出了官方解法function buildMatrix(rows, cols) { const matrix []; for (let i 0; i rows; i) { const row new Array(cols).fill(0); matrix.push(row); } return matrix; }逐行解读这一实现const matrix []初始化外层数组它最终容纳rows个内层行数组for (let i 0; i rows; i)循环恰好执行rows次对应外层长度new Array(cols).fill(0)创建长度为cols的数组并调用fill(0)把每个槽位填充为数值0matrix.push(row)把每一行追加进外层循环结束后return matrix。整体时间复杂度为 O(rows × cols)空间复杂度亦为 O(rows × cols)是该尺寸输入下的最优量级。高频陷阱复用同一行引用初学者最容易踩的坑是写出这样的精简版// 错误示范所有行共享同一个数组引用 function buildMatrix(rows, cols) { return new Array(rows).fill(new Array(cols).fill(0)); }这段代码表面返回的结构与期望完全一致四个用例中的assert.deepEqual甚至也能碰巧通过deepEqual 只看值不看引用。但它隐藏着一个严重缺陷fill()在填充对象/数组类型时填入的是同一个引用外层 2 个元素其实都指向同一块[0, 0, 0]。一旦后续对矩阵做赋值例如matrix[0][1] 9会发现matrix[1][1]也同时变成了 9。freeCodeCamp 课程体系会在矩阵旋转、矩阵移位等后续题目中不断涉及对单元格的原地修改这种共享引用的矩阵会在那时酿成难以排查的 bug。而官方解法每次进入循环体都用new Array(cols)新建行保证行与行之间完全独立。其他等价写法若不想用命令式循环也可以借助Array.from或展开运算符写出等价的函数式实现// 等价方案 AArray.from 每次回调都新建行 function buildMatrix(rows, cols) { return Array.from({ length: rows }, () new Array(cols).fill(0)); } // 等价方案 B先铺外层再映射 function buildMatrix(rows, cols) { return [...Array(rows)].map(() Array(cols).fill(0)); } // 等价方案 C双循环逐元素写入适合后续接自定义初始值 function buildMatrix(rows, cols) { const matrix []; for (let r 0; r rows; r) { matrix[r] []; for (let c 0; c cols; c) { matrix[r][c] 0; } } return matrix; }三个方案各自的关键点一致必须为每一行单独创建一个数组实例。方案 A 与 B 本质相同——Array.from的映射回调与map的回调都会为每个元素执行一次因而天然规避了引用共享方案 C 则把矩阵就是数组的数组这一数据模型用最底层的双层索引显式表达适合在读懂后自行扩展为填充任意值或生成棋盘格。在仓库中的位置矩阵类题目在每日挑战里的延续把视野拉远一点Matrix Builder 并非孤立的矩阵题。在 curriculum/structure/blocks/daily-coding-challenges-javascript.json 的挑战顺序表中可以看到围绕二维数组/矩阵这个数据结构的题目在整套 JavaScript 每日挑战里反复出现例如Challenge 27: Matrix Rotate矩阵旋转Challenge 201: Matrix Shift矩阵移位Challenge 221: Inverted Matrix矩阵翻转Challenge 245: Spiral Matrix螺旋矩阵Challenge 87: Matrix Builder本题构建零矩阵这意味着buildMatrix这类题目承担着打地基的角色先用最简单的零矩阵把行是外层数组、列是内层数组、行间必须独立这三条铁律刻进思维后续的旋转、移位、螺旋遍历等题目才能建立在这个正确的二维数组心智模型之上。从 Markdown 到数据库挑战内容的仓库流转链路这道.md文件并不只是给人看的文本。以本仓库的实现为证据这套每日挑战从课程文件到用户浏览器大致经历如下链路可以用作通读源码的路线图挑战解析与 GraphQL 暴露课程 Markdown 经 curriculum/src/file-handler.ts、get-challenges等工具解析后由前端 Gatsby 层的allChallengeNodeGraphQL 节点对外提供。每日挑战被组织在dev-playgroundsuperblock 下的daily-coding-challenges-javascript与daily-coding-challenges-python两个 block 中这一归属关系在 curriculum/src/build-curriculum.tsdev-playground: SuperBlocks.DevPlayground与 block 结构文件里均有体现。JS/Python 双版本一致性校验curriculum/src/test/daily-challenges.test.js 会同时拉取 JavaScript 与 Python 两个 block 的全部挑战断言两边挑战数量相等且逐对校验id、标题、描述与测试数量完全一致。换句话说Challenge 87 在 Python block 中存在一个同 ID 的同题副本标题同为 Matrix Builder两者由同一条测试兜底保证不会出现内容漂移。数据库投递seed本地或生产环境的数据库通过 tools/daily-challenges/README.md 中描述的流程把挑战灌入DailyCodingChallenges集合先复制sample.env为.env、在开启显示即将上线内容的状态下运行主客户端再在tools/daily-challenges目录执行pnpm seed-daily-challenges。具体实现见 tools/daily-challenges/helpers.ts脚本向http://localhost:8000/___graphql发送 GraphQL 查询按superBlock: dev-playground、block: daily-coding-challenges-javascript拉取全部挑战将 JS 的id、标题、描述、tests、challengeFiles与 Python 的对应字段合并成一条同时携带javascript与python两种实现的数据记录其中description还需经removeSection剥离解析器附加的section标签。前端读取与展示客户端在 client/src/client-only-routes/show-daily-coding-challenge.tsx 中处理challengeType: 28的挑战页面并读取数据库记录的javascript.tests与javascript.challengeFiles渲染题目与判题器。数据库返回结构的合法性由 client/src/utils/daily-coding-challenge-validator.ts 中的 Joi 模式把关——它要求每条记录必须包含challengeNumber不小于 1 的整数、title、date、description以及嵌套的tests[{text, testString}]与challengeFiles[{fileKey, contents}]。本挑战中assert.deepEqual(...)的断言字符串正是存储在tests[].testString里、最终被真实执行的那份代码。这一条链路也解释了为什么本挑战文件中的 frontmatterid、title、challengeType、dashedName、--description--、--hints--、--seed--、--solutions--五个区块缺一不可它们分别对应解析、渲染、判题、初始代码与官方答案五个环节的输入。如何本地验证你的实现该题是纯函数题不依赖任何库因此你可以用任意 JavaScript 运行环境自测。以 Node.js 为例把下面这段完整用例保存为matrix-builder.test.js后执行node matrix-builder.test.js即可function buildMatrix(rows, cols) { const matrix []; for (let i 0; i rows; i) { const row new Array(cols).fill(0); matrix.push(row); } return matrix; } const cases [ [[2, 3], [[0, 0, 0], [0, 0, 0]]], [[3, 2], [[0, 0], [0, 0], [0, 0]]], [[4, 3], [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]], [[9, 1], [[0], [0], [0], [0], [0], [0], [0], [0], [0]]] ]; for (const [[rows, cols], expected] of cases) { const actual buildMatrix(rows, cols); const pass JSON.stringify(actual) JSON.stringify(expected); console.log(buildMatrix(${rows}, ${cols}) -, pass ? PASS : FAIL ${JSON.stringify(actual)}); if (!pass) process.exitCode 1; }说明本地用JSON.stringify做等价比较是因为两个数组即使内部元素完全相同直接也会因引用不同而返回false这与 freeCodeCamp 判题器使用的assert.deepEqual的语义一致——递归比较值而非比较引用。如果你还想观察共享引用陷阱的破坏力可以把上面实现临时替换为return new Array(rows).fill(new Array(cols).fill(0));再追加一行actual[0][0] 9;并打印矩阵——你会发现每一行的第一个元素都被改成了 9。这正是 Challenge 87 想让你在入门阶段就刻进记忆的那一课。【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询