Scratch不是玩具:CS50第一课教你的变量、克隆与计算思维
发布时间:2026/10/22 3:58:23 锦皓数字建站

当我第一次听说 CS50 的 Week 0 用 Scratch 讲课的时候说实话我是有点失望的——我奔着全球公认的硬核编程入门来的结果第一周让我玩积木块但在看完 2024 版的课程并认真做笔记到第三部分之后我必须承认这个设计是对的而且比我想象中要深得多。这份笔记系列已经写到第三篇也就是 CS50 x 2024 Notes Scratch - 03内容从最初的认识积木块推进到了变量、列表、克隆、消息广播这些真正意义上的编程概念。这周我做的核心工作是把 CS50 2024 年第 0 周Scratch 单元后半段的内容完整消化用自己的话重新组织成一套可以照着练的笔记。这篇文章适合两类人一类是正在跟 CS50 进度、觉得 Week 0 有点简单想跳过的人——我建议你先别跳另一类是已经把 Scratch 当游戏玩过、但从没想过它和数据结构、事件驱动这些概念有什么关系的人。这篇笔记会告诉你Scratch 这一课真正的价值从来不在积木本身而在积木背后那些换门语言照样成立的思维模型。1. 为什么用积木做编程启蒙Scratch 不是给小孩玩的那种玩具1.1 去掉语法噪音之后剩下的才是编程CS50 2024 版的 Week 0 开场有一段话让我印象很深大意是你写代码的时候大部分挫折其实来自语法分号、括号、大小写而不是来自解决问题本身。Scratch 把语法层全部去掉了——积木只有能拼在一起才能拼接类型不匹配根本不给你拼的机会。这就像学开车的时候先开自动挡把换挡踩离合的负担拿掉让你专心学看路、判断距离、打方向。这个设计对新人极其友好但对写过几行代码的人反而容易产生轻视心理。我第一遍过 Week 0 的时候就觉得这有什么好学的直到我尝试做 CS50 给的作业做一个互动项目才发现自己连如何让两个角色协作这个最基本的问题都要想半天。语法是可以查的但思路没法查。Scratch 逼你早早就面对思路这件事。1.2 CS50 的 Week 0 到底想让你学会什么把 2024 版的课程内容拆开看Week 0 的核心目标可以归纳成四个词分解decomposition、抽象abstraction、算法algorithms、模式识别pattern recognition。这四件事在不同课程里可能会被包装成计算思维但它本质上就是你能不能把一个复杂问题切开能不能把反复出现的东西提炼成通用工具能不能把解决步骤写清楚到别人或者机器可以照着执行在 Scratch 里做这些练习几乎零成本——你不用先装编译器不用学 Makefile打开网页就能拖积木。而且因为积木的视觉反馈是即时的你可以非常快地验证一个想法到底对不对。这种快速试错的节奏对培养算法直觉特别重要。我的体会是Week 0 不是一个编程课它是一个编程之前的思维训练课只不过恰好用了编程工具来训练。1.3 我这份03笔记的位置在哪里我之前已经完成了两篇笔记第一篇覆盖了 Scratch 界面、角色Sprite、造型Costume、背景Backdrop和基础运动积木第二篇覆盖了事件、顺序执行、重复执行循环和条件判断。到了第三篇也就是这篇文章我处理的是 Week 0 后半段的内容变量与作用域、列表、运算符、克隆机制、消息广播以及一个完整的项目案例。这个位置很关键因为从这里开始Scratch 不再只是让小猫走来走去而是真正进入了用代码组织逻辑的阶段。2. 变量、列表和运算符积木下的真正编程语言2.1 变量的作用域全局与局部CS50 2024 的课程里专门强调了变量在 Scratch 里的两种存在范围适用于所有角色全局变量和**仅适用于当前角色**局部变量。这个概念第一次看可能觉得啰嗦但它其实是后续学 C 语言时全局变量 vs 局部变量的提前预演。用一个小例子就能说明白如果你做一个双人游戏两个角色的得分应该分开记录这时候如果你只用一个全局变量得分两个角色每次加分都会加到同一个数字上游戏逻辑直接崩掉。我当时踩过的坑是给角色 A 建了一个局部变量叫速度然后希望角色 B 也读这个值来控制自己的移动结果角色 B 那边一直是一片空白。因为局部变量对其他角色根本不可见。这个错误给了我一个非常深刻的教训——变量的可见范围是一件必须主动意识的事情而不是运行时自然就有的。在 Scratch 里创建变量的地方在代码标签页的变量积木组点击建立一个变量之后它会问你两个问题名字是什么作用范围是整个项目还是仅当前角色初学者很容易在这里随手选适用于所有角色因为这样省事但省事往往是 bug 的温床。我建议从一开始就养成习惯能被限定的变量一律限定只有确实需要全局共享的比如总分、关卡号、游戏状态才用全局。2.2 列表你还不会数组但你已经用上了列表List在 Scratch 里是一个很神奇的存在。它长得很像变量但它内部可以装一串值。CS50 的课里用它演示了如何保存多个数据——比如一个游戏里收集到的所有道具名字、一段历史对话记录、或者排行榜的分数序列。我第一次看到列表的时候心里想的是这不就是数组吗后来我在笔记里把列表和 C 语言的数组做了一个对照确实如此——只是 Scratch 把索引从 1 开始而 C 从 0 开始。列表的几个基础操作积木分别是添加东西到列表末尾、删除列表中的第 n 项、插入到指定位置、替换某一项以及判断列表是否包含某值。这些操作组合起来可以做出许多看起来很高级的功能。我做了个小实验用列表记录主角走过的所有坐标点然后让一个影子角色按照这些坐标点反向走一遍实现一个简单的回放效果。代码量不算大但效果非常直观。这里有一个很实用的技巧当你需要遍历列表时用列表的长度配合重复执行 n 次n 取长度值然后在循环里用第 i 项取当前项。这个循环结构与 C 里的 for 循环一一对应。我建议在笔记里把这个对应关系写出来它能帮你后面学数组的时候省下一半力气。2.3 常用运算符积木与几个容易翻车的点运算符在 Scratch 里分为几类加减乘除、随机数、比较运算、逻辑运算与/或/不成立、字符串拼接和字符串处理。它们看着简单但实际使用中有几个特别容易翻车的地方。第一个翻车点是随机数的含金量。积木随机取数看起来是在 1 到 10 之间随机取一个数但注意它的边界是闭区间——也就是说 1 和 10 都有可能被取到。这跟你写 C 语言时习惯的左闭右开区间不一致。如果你要模拟1 到 10 但不包括 10的效果就需要写成 1 到 9。第二个翻车点是比较运算的时机。Scratch 的比较运算积木大于、小于、等于只能发生在需要的时候比如放在条件判断里。但你有没有想过如果把一个比较运算的结果直接放在移动 10 步里会发生什么答案是它会被当成 true/false而 Scratch 会把这个布尔值当作 1 或 0 来处理。这在某些版本里可能会导致角色移动 0 步或 1 步的诡异行为调试起来非常困惑。第三个翻车点是字符串拼接的隐式转换。你想把变量和文本拼在一起的时候直接用加入…和…积木就好但如果你拼的是数字和文本页面显示上没有问题后面做数值运算时可能会遇到这个数字到底是字符串还是数字的混乱。Scratch 在这里处理得比较宽容它会自动尝试转换但这种宽容恰恰是隐患——尤其在做一个计分系统时如果你在某个分支里把分数加了一个字符串后面可能整个分数就变成了文本拼接。我建议每个初学者都做一组小实验来验证自己对这些运算符的理解比如写一个猜数字游戏系统随机生成 1 到 100 的整数玩家通过询问积木输入答案程序回答大了/小了/对了。这个项目涵盖了随机数、比较运算、条件判断、循环和变量几乎把这一章所有重要积木都用了一遍。做完这个你才算真正会用运算符。3. 克隆、消息与事件从单个角色到游戏系统3.1 克隆是创建实例不是复制粘贴克隆Clone是我认为 Week 0 后半段最值得深入理解的概念。表面上克隆积木的作用是复制出一个一模一样的新角色但如果你真的把它当成复制粘贴就会做出各种奇怪的游戏——一个克隆体在动另一个克隆体不动你根本找不到原因。更准确的认知是克隆是创建了一个新实例instance它复制的是你点击克隆那一刻那个角色的全部属性位置、方向、造型、变量值但从那之后它就是一个独立对象了。你可以用当作为克隆体启动时积木给它单独编写一套行为逻辑。这跟你在 C 语言里用结构体加循环创建多个对象、在面向对象语言里 new 一个类的实例是同一个思路只是 Scratch 把它藏在了克隆这两个字后面。这里有一个坑克隆体也会响应当 ⚑ 被点击之外的其他全局事件吗会的但克隆体的行为由当作为克隆体启动时触发后才会开始如果你想让每个克隆体都做不同的事你就需要给角色本体和克隆体分别编写逻辑。一个常见的错误是角色本体可能隐藏起来了但克隆体仍然在工作因为克隆体继承的是隐藏状态还是显示状态取决于你克隆时本体是否隐藏。这往往导致调试半天找不到角色到底跑到哪里去了的情况。另外需要注意克隆的数量是有限制的我记得项目里默认克隆体上限大概是 300 个左右具体数值会随项目复杂度变化。如果一个循环里不断生成克隆体而从不清理项目会变得卡顿甚至崩溃。所以规范做法是用完的克隆体主动删除此克隆体并设置合理的生成频率。3.2 广播与事件角色之间怎么打招呼在 Scratch 里角色间通信不是通过变量共享就是通过广播Broadcast来实现的。变量共享适合传数据广播适合发信号。信号的意义在于你不需要关心哪个角色会响应这个信号你只需要把消息丢出去然后任何写了当接收到消息的脚本都会自动执行。这其实就是事件驱动编程Event-Driven Programming的雏形。CS50 里有一个很好的类比广播就像电台发射信号任何打开对应频道的接收器都能收到。你可以创建自定义消息比如玩家死亡、切换到下一关、音符响起然后在不同角色里分别编写响应逻辑。这种设计让每个角色的代码彼此解耦互不干扰。不过这里有个细节容易忽略广播和广播并等待是两个不同的积木。广播并等待会等所有接收到该消息的脚本执行完成之后才继续执行后续代码而普通的广播则是发完就立刻继续往下走。如果你想让敌人角色做出攻击动画之后再结算伤害你就得用广播并等待否则顺序全乱。我在做一个小游戏的时候深刻体会到了这个区别。当时我的逻辑是主角碰到金币 → 广播金币被收集 → 分数增加。我用的普通广播结果发现有时候角色已经消失了分数还没加上去甚至偶尔会重复加分。排查之后发现问题不在于广播本身而在于我在一个循环里连续触发了多次广播。用广播并等待加上一个已收集标志变量才把这个问题解决。3.3 一个完整的小游戏逻辑拆解让我把一个我在笔记中记录的完整项目拆开讲一个简化版的接苹果游戏。玩法是苹果从屏幕上方随机位置掉落玩家用鼠标控制篮子左右移动接住苹果加分苹果落地则减一条命。这个项目把前面提到的所有概念都串起来了。第一步建角色和变量。角色苹果、篮子。全局变量得分、生命。局部变量苹果自己的下落速度。第二步苹果的克隆逻辑。让苹果本体在顶部每隔 1 秒克隆一次自己克隆体启动后随机出现在水平方向的坐标上然后重复执行向下移动 速度 步直到碰到舞台底部边缘或篮子。第三步碰撞检测。在克隆体的循环里加一个判断如果碰到篮子角色就广播接住然后删除此克隆体。接住消息由篮子脚本接收负责把得分增加 1。第四步掉落判定。如果苹果的 y 坐标小于底部边界同样删除克隆体并让生命值减 1。这里要注意苹果落地的判断和碰到篮子的判断一定要互斥否则可能出现一个克隆体同时触发两种逻辑。这个拆解看起来很简单但里面每一个环节都有一个值得思考的点比如为什么要用克隆而不是直接创建多个苹果角色因为克隆让代码只需要维护一份。为什么要用广播而不是直接让克隆体去修改得分因为修改得分是篮子角色或者全局逻辑该做的事不属于苹果的职责。这种谁负责什么的边界感就是抽象思维的雏形。4. 调试训练CS50 真正想给你的是排错直觉4.1 三段式排查复现、定位、验证CS50 的 Week 0 并没有专门开一个章节讲调试但你做作业的时候实际上就在被训练调试。我自己的经验是不管是什么语言什么环境调试都逃不开三步稳定复现、缩小范围、验证修复。耐人寻味的是Scratch 的调试体验比很多文本语言还要好因为它每一步都有可视化反馈。你可以用等待 0.5 秒积木把循环放慢观察每一帧发生了什么可以临时在代码里插入说…积木把某个变量的值实时显示在角色头顶甚至在变量积木上勾选在舞台上显示直接看到所有变量的当前状态。这些手段组合起来很快就能把问题定位到某几块积木上。4.2 我在练习中遇到的四个典型 bug第一个 bug看似没反应。我写了一段代码按下空格键角色移动 10 步。结果按了空格一点都不动。检查后发现我用的不是当按下空格键事件而是把它放在了一个当 ⚑ 被点击的脚本里但那个脚本前面有一个等待 3 秒积木所以角色要等三秒才动。这不是 bug 是顺序问题但它提醒我事件和顺序是两个维度先搞清楚你的代码是被什么触发的。第二个 bug幽灵加分。前文提到的金币重复加分问题。根源是碰撞检测放在了一个没有退出条件的重复循环里角色虽然已经碰到金币并广播了消息但循环下一次迭代时碰撞条件依然成立导致连续广播。解决方法是立刻删除金币克隆体或设置一个标志位。第三个 bug角色消失了。我让一个角色碰到舞台边缘就隐藏但没想到它隐藏之后仍然持续响应键盘事件导致玩家感觉角色死了但还能操作。排查方式是打开角色的外观检查发现它的显示/隐藏状态和实际行为不一致。解决办法是在隐藏后加一个等待 1 秒后再显示或者直接用一个游戏状态变量来控制所有交互。第四个 bug列表越界。用第 n 项取列表内容时如果 n 超过了列表长度Scratch 不会像 C 语言那样直接报错而是返回空值。这个不报错比报错更危险因为你很难发现。我当时做的是一个答题记录功能结果因为删除了一项但没有同步更新循环次数导致后面几道题的答案全部变成了空白。这四个 bug 类型——顺序问题、重复触发、状态与表现不一致、静默容错——我觉得值得记下来。它们不是 Scratch 特有的在 C、Python、JavaScript 里都会以类似形态出现。你在 Scratch 阶段就把这四类问题都见一遍后面的课程会轻松很多。4.3 调试思维如何迁移到后续课程很多学习者在从 Scratch 跳到 C 之后最痛苦的不是语法而是出了 bug 不知道从哪里开始查。C 环境的调试手段比 Scratch 粗糙得多——没有可视化没有说积木唯一的工具是 printf。但如果你在 Scratch 阶段已经养成了复现→缩小范围→验证的思维习惯迁移过程就只是换工具而已。我在笔记里给自己写了一条规则每遇到一个 bug不管多简单都要记录三行——现象、原因、修复方法。一个月之后回看你会发现自己犯的错误高度集中在那几个类型上。这是最容易见效的自我改进方式比盲目做更多项目有用得多。5. 把积木翻译成代码Scratch 与 C 的对照表5.1 概念映射表到这一节我不想再空谈概念而是给出一张我自己整理的对照表。这张表的作用是让你在学 Week 0 的时候就提前知道这些积木在后面的 C 语言课程里会变成什么。它不是我发明的很多学习社区都有类似总结但我用自己的话重新组织了一遍加上了我实际踩过的坑。Scratch 积木 / 概念对应 C 语言概念我的实操提醒当 ⚑ 被点击main() 函数入口Scratch 只有一个主入口C 也是但 C 里你还得关心程序何时结束变量适用于所有角色全局变量全局变量在 C 里同样容易引发谁都能改的混乱变量仅适用于当前角色局部变量 / 静态变量注意生命周期C 的局部变量每次函数调用都重新创建列表数组 / 链表Scratch 索引从 1 开始C 数组从 0 开始转换时别忘减一循环重复执行 n 次 / 无限循环for / while无限循环在 C 里要自己保证退出条件Scratch 拖一个积木就行条件判断如果…那么…否则if / else逻辑完全对应但 C 有括号注意作用域广播函数调用 / 回调 / 信号量广播是异步的解耦通信函数调用是同步的语义上有本质差异克隆动态内存分配 / 结构体数组克隆体上限对应内存上限都是资源管理的第一个教训运算符随机数rand() 函数C 的 rand() 默认区间是 0 到 RAND_MAX需要取模才能限制范围重复执行直到do…while条件判断的先后关系是唯一差别这张表的价值不在于背下来而在于建立映射。我在学 C 语言 Week 1 的时候每学一个新语法就先回到这张表看看它在 Scratch 里长什么样。这个逆向学习方法几乎帮我节省了一半预习时间。5.2 为什么这种翻译练习值得做有些人可能会说反正都要学 C 了为什么还要费劲做这种翻译练习我的回答是翻译练习的本质不是转换语法而是确认概念本身你已经懂了。如果你能在 Scratch 里熟练使用克隆和广播那么学 C 的时候遇到结构体数组和函数指针就只是换一套词而已底层逻辑你没见过的概率很小。反过来如果你直接跳进 C 去学这些概念你要同时面对两个未知概念本身和语法细节。两件未知叠加就是劝退的起点。5.3 我给自己的验收标准每个学习阶段都应该有验收标准不能光靠感觉学会了。我给 Scratch 阶段定的验收标准是能不开课程视频、不查积木说明独立完成以下四个项目——猜数字游戏、接苹果游戏、一个带计分和生命值的双人小游戏、一个能用列表记录并回放操作序列的 demo。这四个项目分别对应变量、克隆、广播和列表做完它们你才真正达到 CS50 Week 0 的实战要求。6. 这套笔记我自己怎么用 推荐的练习路径6.1 笔记格式与复盘方法写到第三篇笔记我的格式已经稳定下来了分享给你参考。每一篇笔记分四个部分概念清单这周出现了哪些新概念每个概念一句话解释、积木地图用到的积木分组列表标注哪些是新的哪些复习、踩坑记录每个 bug 的三行式记录、项目成品做完的项目录制一段 30 秒以内的演示视频顺便用文字描述核心逻辑。复盘的时候我一般用费曼式提问假装自己要给完全没接触过编程的人讲清楚什么是克隆或者为什么要用广播。如果我能流畅地讲出来并给对方举一个生活中的类比就说明这个概念真的消化了。如果讲到一半卡住那个卡住的位置就是需要重新学习的薄弱点。6.2 一周练习清单如果你现在正准备开始 CS50 的 Scratch 部分或者在做笔记中途停滞了这份一周清单可以作为参考。第一天看完 Week 0 视频中后半段变量到列表部分同步写下概念清单第二天用猜数字项目专门训练变量和运算符第三天研究克隆机制做一个 10 个克隆体同时运动的小实验第四天做接苹果项目并刻意使用广播第五天给项目加入列表功能比如记录最高分与名字第六天复盘所有 bug 记录给每个 bug 归类第七天休息但翻一遍自己的笔记试着不看屏幕说出每一个概念的定义。6.3 最后再分享一个小技巧我用了很久才发现的一个小而有效的技巧在 Scratch 里做项目时先把变量是否显示在舞台这个开关全部打开。这样你在测试的时候能直接看到每个变量的实时数值而不是等到出 bug 才去翻代码。很多看似诡异的现象比如分数突然变成 0、某些角色的位置不对看到变量面板上的数值变化原因一下子就明白了。这个习惯我一直沿用到现在写代码只不过把变量面板换成了调试器里的 Watch 窗口。笔记写到03我自己能明显感觉到思维方式在变——从一个拖积木让猫跑的新手变成了用概念拆解问题的入门者。Scratch 阶段的这些积累后面学 C 的时候会一个接一个地兑现。希望这份笔记对你也有同样的价值。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。