1.1. 第 0 周:Scratch¶
备注
本章改编自 CS50x 官方讲义笔记——哈佛大学的 CS50's Introduction to Computer Science (David J. Malan 主讲),基于 2026 年版本。原课程材料以 CC BY-NC-SA 4.0 许可开放;本中文改编版为非官方译作,与哈佛大学及 CS50 团队无关,也未获其背书。课程主页:cs50.harvard.edu/x;英文原稿:CS50x Week 0 notes。
1.1.1. 欢迎!¶
人工智能(AI)正在为计算机科学和整个世界带来新的进步与激情!
尽管 AI 进步巨大,有时甚至消除了拖慢流程的人类瓶颈,但当你能够理解、创造并组织代码时,你就成了车手、飞行员——一个被编程赋能的创造者。
然而,与其想着把 AI 当作免除学习基础知识的借口,不如考虑:掌握扎实的基础、再借助 AI 的赋能,会为你和你所服务的人打开怎样全新的机会。
1.1.2. Visual Studio Code 与 chat.py¶
VS Code 是一个 IDE (集成开发环境),我们可以在其中编写代码。
为了让你提前尝到后面的甜头,我们可以动手做一个自己的聊天机器人
chat.py。在一台已经配置好 OpenAI 库的机器上,我们可以这样编程。
在文本编辑器中,输入以下代码:
from openai import OpenAI client = OpenAI() response = client.responses.create( input="In one sentence, what is CS50?", model="gpt-5" ) print(response.output_text)
注意我们导入了 OpenAI 的一个库,从而获得它的各种能力。先创建一个聊天客户端,然后把称为
input的问题交给client去回答,最后打印出response。我们可以改进这段代码,让用户自己提问。把代码修改如下:
from openai import OpenAI client = OpenAI() prompt = input("Prompt: ") response = client.responses.create( input=prompt, model="gpt-5" ) print(response.output_text)
注意现在多了
prompt,用户因此可以提出自己的问题。更进一步,这段程序还可以提供一个
system_prompt,给聊天机器人更多的上下文与指令:from openai import OpenAI client = OpenAI() user_prompt = input("Prompt: ") system_prompt = "Limit your answer to one sentence. Pretend you're a cat." response = client.responses.create( input=user_prompt, instructions=system_prompt, model="gpt-5" ) print(response.output_text)
注意
system_prompt被用来提供额外的上下文与指令。借助编程,仅仅十行代码就能构建出非常强大的程序!
我们造了自己的 橡皮鸭 —— CS50 Duck,用它在本课程中获取帮助。
请牢记我们的学术诚信政策:除 CS50 Duck 外,禁止使用任何其他 AI 工具。
1.1.3. 计算机科学与问题解决¶
本质上,计算机编程就是接收输入、产出输出——从而解决问题。输入与输出之间发生的、可称为 黑盒 的部分,正是本课程要探讨的重点。
举例来说,我们需要统计班级出勤。可以用“一元计数法”(也叫 base-1 ),也就是扳手指头一个一个地数。
如今计算机用的是 二进制 (也叫 base-2 )。我们熟悉的“位(bit)”一词,正是从 “二进制数字(binary digit)”演化而来。一个 位 要么是 0,要么是 1:要么关,要么开。
计算机的世界只有 0 和 1。0 代表“关”,1 代表“开”。计算机内部有数百万甚至数十亿个不断开关的晶体管。
想象用一个灯泡来计数,一个灯泡只能表示 0 或 1 两种状态。
但如果有三个灯泡,能表示的信息就更多了!
在你的设备(如 iPhone 或电脑)里,有数以百万计被称为 晶体管 的“比喻式灯泡”,支撑着我们每天习以为常的各种操作。
作为启发式的理解,我们可以把下面每一列当作 二进制位 的一个数位:
4 2 1
用三个灯泡,下面可以代表零:
4 2 1 0 0 0
类似地,下面将代表一:
4 2 1 0 0 1
按这个逻辑,下面等于二:
4 2 1 0 1 0
继续这个逻辑,下面代表三:
4 2 1 0 1 1
四将显示为:
4 2 1 1 0 0
事实上,仅用三个灯泡我们就能数到七!
4 2 1 1 1 1
计算机用以 2 为基数的方式计数,可以这样表示:
2^2 2^1 2^0 4 2 1
因此可以说,需要三个比特位(分别代表 4、2、1)才能表示最大为 7 的数字。
同理,要数到八,数值会这样表示:
8 4 2 1 1 0 0 0
计算机通常用八个比特位(也叫一个 字节 )来表示一个数字。例如,
00000101是二进制里的 5;11111111表示 255。而零可以想象成:128 64 32 16 8 4 2 1 0 0 0 0 0 0 0 0
1.1.4. ASCII¶
正如数字是 0 和 1 的二进制模式,字母同样用 0 和 1 来表示!
由于表示数字和字母的 0、1 存在重叠,人们制定了 ASCII 标准,把特定的字母映射到特定的数字。
例如,字母
A被约定映射到数字 65;01000001就是二进制里的 65。可以这样可视化:128 64 32 16 8 4 2 1 0 1 0 0 0 0 0 1
如果你收到一条短信,其底层二进制数据可能对应着 72、73 和 33 这几个数字。把它们映射到 ASCII,信息就会是这样:
H I ! 72 73 33
多亏了像 ASCII 这样的标准,我们才能对这些数值达成共识!
这是一张扩展的 ASCII 值表:
0 NUL 16 DLE 32 SP 48 0 64 @ 80 P 96 ` 112 p 1 SOH 17 DC1 33 ! 49 1 65 A 81 Q 97 a 113 q 2 STX 18 DC2 34 ” 50 2 66 B 82 R 98 b 114 r 3 ETX 19 DC3 35 # 51 3 67 C 83 S 99 c 115 s 4 EOT 20 DC4 36 $ 52 4 68 D 84 T 100 d 116 t 5 ENQ 21 NAK 37 % 53 5 69 E 85 U 101 e 117 u 6 ACK 22 SYN 38 & 54 6 70 F 86 V 102 f 118 v 7 BEL 23 ETB 39 ’ 55 7 71 G 87 W 103 g 119 w 8 BS 24 CAN 40 ( 56 8 72 H 88 X 104 h 120 x 9 HT 25 EM 41 ) 57 9 73 I 89 Y 105 i 121 y 10 LF 26 SUB 42 * 58 : 74 J 90 Z 106 j 122 z 11 VT 27 ESC 43 + 59 ; 75 K 91 [ 107 k 123 { 12 FF 28 FS 44 , 60 < 76 L 92 108 l 124 | 13 CR 29 GS 45 - 61 = 77 M 93 ] 109 m 125 } 14 SO 30 RS 46 . 62 > 78 N 94 ^ 110 n 126 ~ 15 SI 31 US 47 / 63 ? 79 O 95 _ 111 o 127 DEL如果你愿意,可以进一步了解 ASCII (参见 维基百科上的 ASCII 词条)。
如果每个字符恰好占一个 8 位字节,最多可编码 256 个不同的字符。而 ASCII 只用了其中的 128 个(0–127)。
1.1.5. Unicode¶
随着时代发展,用文本交流的方式越来越多。
由于二进制编码的位数有限,无法表示人类使用的全部字符,因此 Unicode 标准扩展了计算机可传输与理解的位数。Unicode 不仅包含特殊字符,也包含表情符号。
有些表情符号你可能每天都在用。下面这些你大概很熟悉:
😀 😃 😄 😁 😆 😅 😂 🙂 🙃 😉 😊 😇 😍 😘 😗 😙 😚 😋 😛 😜 😝 🤑 🤓 😎 🤗 😏 😶 😐 😑 😒 🙄 😬 😕 ☹️ 😟 😮 😯 😲 😳 😦 😧 😨
虽然 0、1 的模式在 Unicode 中是标准化的,但各设备厂商对同一个表情符号的渲染可能略有不同。
越来越多的字符与表情正被加入 Unicode 标准。
如果你愿意,可以进一步了解 Unicode (参见 维基百科上的 Unicode 词条)。
如果你愿意,可以进一步了解 表情符号。
1.1.6. RGB¶
0 和 1 也可以用来表示颜色。
红、绿、蓝(即
RGB)是三个数字的组合(三个色块,数值为 72、73、33)。之前表示
HI!的三个数字 72、73、33,若被图像程序解读,则是一种浅黄色(一个黄色色块):红分量为 72,绿分量为 73,蓝分量为 33。表示红、绿、蓝各种颜色所需的三个字节,构成了数字图像中每一个 像素 (色点)。图像说白了就是 RGB 值的集合。
0 和 1 还可以表示图像、视频和音乐!
视频就是一连串图像存放在一起,就像翻页动画书。
音乐同样可以用各种字节组合来表示。
1.1.7. 算法¶
解决问题是计算机科学与计算机编程的核心。算法 就是一步一步解决问题的指令集合。
想象一个基本问题:在电话号码簿里查找一个人的名字。
你会怎么做?
一种办法是从第 1 页开始,一页一页往后翻,直到最后一页。
另一种办法是一次翻查两页。
最后一种、也许更好的办法是:翻到电话簿中间,问自己要找的名字在左边还是右边,然后重复这个过程,每次把问题砍掉一半、一半、再一半。
以上每种做法都可以称为算法。每种算法的速度可以用所谓的 大 O 记号 绘制成图来直观表示:
注意第一种算法(图中红色曲线)的大 O 是
n:如果电话簿里有 100 个名字,最多可能要找 100 次。第二种一次查两页的算法,大 O 是n/2,因为我们快了一倍。最后一种算法的大 O 是 log2 n:即使问题规模翻倍,也只多花一步。程序员把基于文本的人类指令翻译成 代码 来解决问题。
1.1.8. 伪代码¶
伪代码是人类可读的指令,常用来描述算法的步骤。
编制 伪代码 的能力,是你在本课程乃至编程领域成功的关键。
例如,针对上面的第三种算法,我们可以这样写伪代码:
1 Pick up phone book 2 Open to middle of phone book 3 Look at page 4 If person is on page 5 Call person 6 Else if person is earlier in book 7 Open to middle of left half of book 8 Go back to line 3 9 Else if person is later in book 10 Open to middle of right half of book 11 Go back to line 3 12 Else 13 Quit
写伪代码是一项极其重要的技能,至少有两个原因。第一,在正式编码之前先写伪代码,能让你提前把问题的逻辑想清楚。第二,写好伪代码后,你可以把它讲给他人,帮助他们理解你的编码决策与代码原理。
注意我们的伪代码里有几个独特之处。首先,有些行以动词开头,如 pick up 、 open 、look at 。稍后我们会把这些称为 函数 。
其次,注意有些行包含
if或else if这样的语句。这些被称为 条件 。第三,注意有些表达式可以被判定为 真 或 假 ,比如“person is earlier in the book”。我们把这些称为 布尔表达式 。
最后,注意有像“go back to line 3”这样的语句。我们把这些称为 循环 。
这些构件正是编程的基本功。
在下面要讲的 Scratch 中,我们就会用到上面每一个基本构件。
1.1.9. 后续内容¶
本周你将学习 Scratch,一种可视化编程语言。
然后在未来的几周,你将学习 C 语言。它看起来像这样:
#include <stdio.h> int main(void) { printf("hello, world\n"); }
学好 C,会让你为将来学习 Python 等其他编程语言做好更充分的准备。
请注意,程序员正是借助 抽象 在前人成果之上不断建造的:人们没有直接用 0 和 1 编程,而是发明了编程语言,把极其艰难的二进制编程不断 抽象 掉,换成一层层越来越易用的语言。我们得以站在巨人的肩膀上!
1.1.10. Scratch¶
Scratch 是由 MIT 开发的可视化编程语言。
Scratch 使用的正是我们本讲前面讲过的那些核心编程构件。
Scratch 是入门编程的好方式:它让你以可视化方式摆弄这些构件,而无需担心花括号、分号、圆括号之类恼人的语法。
Scratch 的
IDE(集成开发环境)长这样:
注意左侧是一个 积木块 调色板,供你编程使用;紧挨积木块右侧,是可以把积木拖进去搭建程序的操作区;再往右则是小猫所站的 舞台——你的程序在此活起来。
Scratch 使用如下坐标系:

注意舞台中心是坐标 (0,0)。此刻小猫正好位于这个位置。
1.1.11. Hello World¶
开始时,把“when green flag clicked”积木拖到编程区。再把
say积木拖过去,接到上一块下面。when green flag clicked say [hello, world]
注意点击舞台上的绿旗后,小猫就会说“hello, world”。
这很好地印证了我们前面关于编程的讨论:

注意输入
hello, world被传给函数say,而该函数运行产生的 副作用 就是小猫说出hello, world。
1.1.12. Hello, You¶
我们可以让程序更具交互性,让小猫对特定的人说
hello。把程序改成这样:when green flag clicked ask [What's your name?] and wait say (join [hello,] (answer))
注意点击绿旗后,函数
ask会运行。程序向你(用户)提问What's your name?然后把这个名字存进称为answer的 变量 。接着程序把answer传给一个叫join的特殊函数,它把两段文本hello和所提供的名字拼接起来。answer的值作为实参传给join,这些一起再传给say函数。于是小猫会说Hello,加上一个名字。你的程序现在有了交互。在整个课程中,你都会给算法提供输入、获得输出。就用上面这个程序,可以这样表示:

注意输入
hello,和answer交给join,它返回hello, David。这个返回值再传给say,产生小猫说话的 副作用 。类似地,我们可以把程序改成这样:
when green flag clicked ask [What's your name?] and wait speak (join [hello,] (answer))
注意这个程序在点击绿旗时,把同样的变量与
hello拼接后,交给一个名为speak的函数。
1.1.13. 喵、循环与抽象¶
与写伪代码并列,抽象 是计算机编程中一项至关重要的技能与概念。
抽象,就是把一个大问题一步步简化成越来越小的问题。
例如,如果你要为朋友们举办一场盛宴,独自烹制整桌菜这个 问题 可能让人倍感压力!但如果你把做菜拆解成一个又一个更小的任务(问题),做出这顿美味大餐这件大事就没那么难了。
在编程中,甚至在 Scratch 里,我们都能看到抽象的实际运用。在编程区,先这样写:
when green flag clicked play sound (Meow v) until done wait (1) seconds play sound (Meow v) until done wait (1) seconds play sound (Meow v) until done
注意你在反反复复做同一件事。确实,如果你发现自己在重复编写同样的语句,多半可以更巧妙地编程——把这些重复的代码抽象出去。
你可以把代码改成这样:
when green flag clicked repeat (3) play sound (Meow v) until done wait (1) seconds
注意这个循环做的和之前完全一样。但通过把重复抽象成一个会自动 重复 代码的积木块,问题被简化了。
我们甚至可以用
define积木更进一步,创建你自己的积木(你自己的函数)!这样写:define meow play sound (Meow v) until done wait (1) seconds when green flag clicked repeat (3) meow
注意我们定义了自己的积木块
meow。这个函数播放meow声音,然后等待一秒。下面可以看到,点击绿旗后,我们的 meow 函数被重复了三次。我们甚至可以让函数接收一个输入
n,从而重复指定次数:define meow n times repeat (n) play sound [meow v] until done wait (1) seconds
注意
n取自“meow n times”。n通过define积木传给 meow 函数。总之,注意这个逐步打磨的过程如何带来了越来越好的代码设计。还要注意,我们创造了自己的算法来解决问题。整个课程中,你都会反复锻炼这两项技能。
1.1.14. 条件¶
条件 是编程的基本构件之一,程序借此判断某个特定条件是否成立。若条件成立,程序就做某件事。
为演示条件,先这样写:
when green flag clicked forever if <touching (mouse-pointer v)?> then play sound (Meow v) until done
注意这里用到了
forever积木,让if积木一遍遍反复触发,从而持续检查小猫是否碰到了鼠标指针。我们可以把程序改成这样,以接入视频侦测:
when video motion > (10) play sound (Meow v) until done
记住,编程往往是一个试错的过程。如果你感到沮丧,请花时间跟自己梳理眼前的问题:你现在处理的到底是什么具体问题?什么是对的?什么是错的?
1.1.15. Oscartime¶
Oscartime Embedded
Oscartime 是大卫自己写的一个 Scratch 程序——不过这段音乐可能会萦绕着他,因为做这个程序时他听了太多遍。花点时间亲自玩玩这个 Oscartime 游戏。
我们来亲手搭建 Oscartime,先加上路灯。

然后,这样写代码:
when green flag clicked switch costume to (oscar1 v) forever if <touching (mouse-pointer v)?> then switch costume to (oscar2 v) else switch costume to (oscar1 v)
注意把鼠标移到 Oscar 身上,就会改变它的服装。你可以 浏览这些代码块 了解更多。
接着,把代码改成这样,做出一个下落的垃圾:
when green flag clicked set drag mode [draggable v] go to x: (pick random (0) to (240)) y: (180) forever change y by (-1) end
注意垃圾在 y 轴上的位置总是从 180 开始,x 位置则是随机的。只要垃圾还在地板之上,它每次就下落 1 像素。你可以 浏览这些代码块 了解更多。
然后,把代码改成这样,允许拖动垃圾。
when green flag clicked forever if <touching [Oscar v] ?> then go to x: (pick random (0) to (240)) y: (180) end end你可以 浏览这些代码块 了解更多。
接下来,我们这样实现计分变量:
when green flag clicked set drag mode [draggable v] go to top forever change y by (-1) end
when green flag clicked forever if <touching [Oscar v] ?> then go to top end enddefine go to top go to x: (pick random (0) to (240)) y: (180)
你可以 浏览这些代码块 了解更多。
去玩玩完整的游戏 Oscartime 吧。
1.1.16. Ivy's Hardest Game¶
从 Oscartime 转到 Ivy's Hardest Game,现在我们可以想象如何在程序中实现移动。
我们的程序有三个主要部分。
首先,这样写代码:
when green flag clicked go to x: (0) y: (0) forever listen for keyboard feel for walls
注意点击绿旗后,我们的角色先移动到舞台中心坐标 (0,0),然后永远地监听键盘、感知围墙。
其次,加上这第二组积木:
define listen for keyboard if <key (up arrow v) pressed?> then change y by (1) end if <key (down arrow v) pressed?> then change y by (-1) end if <key (right arrow v) pressed?> then change x by (1) end if <key (left arrow v) pressed?> then change x by (-1) end
注意我们创建了自定义的
listen for keyboard脚本。对键盘上的每一个方向键,它都会让角色在屏幕上移动。最后,加上这一组积木:
define feel for walls if <touching (left wall v) ?> then change x by (1) end if <touching (right wall v) ?> then change x by (-1) end
注意我们还有一个自定义的
feel for walls脚本。当角色碰到墙时,就把它移回安全位置——防止它走出屏幕。你可以 浏览这些代码块 了解更多。
Scratch 允许一次在屏幕上放置多个角色。
再加一个角色,把下面的积木加到你的程序里:
when green flag clicked go to x: (0) y: (0) point in direction (90) forever if <<touching (left wall v)?> or <touching (right wall v)?>> then turn right (180) degrees end move (1) steps end
注意耶鲁的角色来回走动,似乎挡了哈佛的角色的路。当它撞到墙时就转身,直到再次撞墙。你可以 浏览这些代码块 了解更多。
你甚至可以让一个角色跟随另一个角色。再加一个角色,把下面的积木加到程序里:
when green flag clicked go to (random position v) forever point towards (Harvard v) move (1) steps
注意现在 MIT 的标志好像在追着哈佛的标志跑。你可以 浏览这些代码块 了解更多。
去试试完整的游戏 Ivy's Hardest Game 吧。
1.1.17. 总结¶
在本课中,你了解了本课程在广阔的计算机科学与编程世界中的位置。你学到了……
虽然 AI 能消除人类瓶颈,但学好计算机科学的基础知识与编程的基本构件,才能让你越来越善用这些新兴技术。
在本课程中使用 AI 的合理与不合理方式。
解决问题,才是计算机科学家的真谛。
计算机如何理解并表示数字、文本、图像、音乐和视频。
伪代码这一基本编程技能。
抽象将如何在你未来的课程学习中发挥作用。
编程的基本构件,包括函数、条件、循环和变量。
如何在 Scratch 中搭建一个项目。
这就是 CS50!欢迎登船,下次见!
