第 0 讲:Scratch
欢迎!
这门课讲的不只是计算机编程!你将在这门课中学到的实用技能,可能会在计算机科学之外,对你的生活和学习产生深远影响。
更准确地说,这门课关注的是一种非常有力量的问题解决方式!你在这里学到的解决问题的方法,很可能马上就能用于这门课之外的工作,甚至贯穿你的整个职业生涯!
不过,这并不轻松!在这门课里,你会像“对着消防水管喝水”一样接收大量知识。接下来的几周里,你会惊讶于自己能够完成多少事情。
这门课更重要的目标,是让“今天的你”不断向前进步,而不是去达到某个想象中的统一标准。
课程一开始最重要的提醒是:给自己足够的时间来学习这门课。每个人的学习方式都不同。如果一开始有些事情不顺利,请记住,只要投入时间,你的能力会不断成长。
如果这是你的第一门计算机科学课程,不要害怕!对大多数同学来说,这也是他们的第一门计算机科学课程!此外,助教、课程助理和同伴社区都会在这里帮助你!
社区!
你是一个学习社区的一员,这个社区包括 Harvard College、Harvard Extension School 以及通过 edX.org 学习本课程的同学。
我们希望你能参加 CS50 Puzzle Day 和 CS50 Fair,无论是线下还是线上。
如果你是 Harvard 校园里的学生,还可以参加 CS50 Lunches 和 CS50 Hackathon。
计算机科学与问题解决
从本质上说,计算机编程就是接收某些输入,并产生某些输出,从而解决问题。输入和输出之间发生了什么,也就是我们可以称为黑盒的部分,正是这门课的重点。
例如,我们可能需要为一节课点名。我们可以使用一种叫作一元制(也叫一进制)的系统,每次数一根手指。
今天的计算机使用一种叫作二进制的系统来计数。我们熟悉的术语 bit 来自 binary digit。一个 bit 就是 0 或 1:开或关。
计算机只用 0 和 1 来表达信息。0 表示关,1 表示开。计算机由数以百万计、甚至数以十亿计的晶体管组成,这些晶体管不断地打开和关闭。
如果把它想象成灯泡,一个灯泡只能从 0 数到 1。
但是,如果你有三个灯泡,就会有更多可能!
在你的 iPhone 里,有数百万个类似灯泡的元件,叫作晶体管,正是它们让这个设备能够完成我们每天习以为常的各种操作。
作为一种帮助理解的方法,我们可以把下面的数值想象成二进制位中每一位所代表的值:
4 2 1
使用三个灯泡,下面这种状态可以表示 0:
4 2 1
0 0 0
类似地,下面这种状态表示 1:
4 2 1
0 0 1
按照这个逻辑,我们可以认为下面这种状态等于 2:
4 2 1
0 1 0
继续推广这个逻辑,下面这种状态表示 3:
4 2 1
0 1 1
4 会表示为:
4 2 1
1 0 0
事实上,只用三个灯泡,我们就能数到 7!
4 2 1
1 1 1
计算机使用二进制来计数。可以这样表示:
2^2 2^1 2^0
4 2 1
- 因此,可以说要表示最大为 7 的数字,需要三个 bit(4 位、2 位和 1 位)。
同样,要数到 8,各位的值可以表示如下:
8 4 2 1
1 0 0 0
计算机通常使用八个 bit(也叫一个 byte)来表示一个数字。例如,00000101 是二进制中的数字 5。11111111 表示数字 255。你可以把 0 想象成下面这样:
128
64
32
16
8
4
2
1
0
0
0
0
0
0
0
0
ASCII
正如数字可以表示为由 1 和 0 组成的二进制模式,字母也可以用 1 和 0 来表示!
由于表示数字和字母时都会用到 1 和 0,于是人们创建了 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。
由于一个 byte 的二进制最多只能数到 255,ASCII 能表示的字符数量也因此受到限制。
Unicode
随着时间推移,人们通过文本交流的方式越来越多。
由于原有的二进制位数不足以表示人类使用的各种字符,Unicode 标准扩展了计算机可以传输和理解的 bit 数量。Unicode 不仅包含特殊字符,也包含 emoji。
有些 emoji 你可能每天都会用到。下面这些也许很眼熟:
😀 😃 😄 😁 😆 😅 😂 🙂 🙃 😉 😊 😇 😍 😘 😗 😙 😚 😋 😛 😜 😝 🤑 🤓 😎 🤗 😏 😶 😐 😑 😒 🙄 😬 😕 ☹️ 😟 😮 😯 😲 😳 😦 😧 😨
虽然 0 和 1 的模式在 Unicode 中是标准化的,但不同设备厂商显示同一个 emoji 的样子可能会略有不同。
Unicode 标准还在不断加入更多功能,以表示更多字符和 emoji。
如果愿意,你可以进一步了解 Unicode。
如果愿意,你也可以进一步了解 emoji。
RGB
- 0 和 1 也可以用来表示颜色。
红、绿、蓝(称为 RGB)由三个数字组合而成。
以前面用过的 72、73 和 33 为例,它们在文本中表示 HI!,但如果由图像读取器解释,就会被看作一种浅黄色。其中红色值是 72,绿色值是 73,蓝色值是 33。
表示红、绿、蓝(也就是 RGB)各种颜色所需的三个 byte,构成了任何数字图像中的每个像素(或点)。图像本质上就是 RGB 值的集合。
0 和 1 可以用来表示图像、视频和音乐!
视频是许多图像按顺序存储在一起,就像翻页动画书一样。
音乐也可以用不同 byte 的组合以类似方式表示。
算法
问题解决是计算机科学和计算机编程的核心。算法就是一组逐步执行的指令,用来解决某个问题。
想象一个基本问题:试图在电话簿中找到某个名字。
可以怎样做呢?
一种方法是从第一页开始,一页接一页地读,直到最后一页。
另一种方法是每次查两页。
最后一种,也可能更好的方法,是翻到电话簿中间,然后问:“我要找的名字在左边还是右边?”接着重复这个过程,一次又一次把问题规模减半。
这些方法都可以称为算法。我们可以用所谓的 big-O 表示法来描述每种算法的速度:
注意,第一个算法(红色高亮)的大 O 是 n,因为如果电话簿里有 100 个名字,最多可能需要尝试 100 次才能找到正确的名字。第二个算法每次查两页,所以大 O 是 n/2,因为我们查页的速度快了一倍。最后一个算法的大 O 是 log2n,因为即使问题规模翻倍,也只会多出一步来解决问题。
- 程序员会把面向人的文本指令翻译成代码。
伪代码
这种把指令转换为代码思路的过程称为伪代码。
编写伪代码的能力,对于学好这门课以及掌握计算机编程都非常关键。
伪代码是代码的一种人类可读版本。例如,以上面的第三种算法为例,我们可以写出如下伪代码:
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这样的语句。这些叫作条件语句。第三,注意有些表达式可以判断为 true 或 false,例如“person is earlier in the book”。我们称它们为布尔表达式。
最后,注意还有像“go back to line 3”这样的语句。我们称它们为循环。
这些构件就是编程的基础。
在下面要讨论的 Scratch 中,我们会用到上述每一种编程基础构件。
人工智能
思考一下,我们怎样利用上面的构件来开始创建自己的人工智能。请看下面的伪代码:
If student says hello
Say hello
Else if student says goodbye
Say goodbye
Else if student asks how you are
Say well
Else if student asks why 111 in binary is 7 in decimal
...
注意,仅仅为了编写少数几种交互,就已经需要很多行代码。如果要处理成千上万甚至数万种可能的交互,又需要多少行代码呢?
AI 程序员不会像上面那样逐条编写对话式 AI,而是使用大型数据集训练大型语言模型(LLMs)。
LLMs 会观察大量语言文本中的模式。这类语言模型会尝试对哪些词会接在另一些词之后、或与另一些词一起出现做出最佳猜测。
虽然基于 AI 的软件在生活和工作中的许多方面都很有用,但我们规定,使用 CS50 自有工具以外的 AI 软件是不合理的。
CS50 自有的 AI 软件工具叫作 CS50.ai,它是你在本课程中可以使用的 AI 助手。它会帮助你,但不会直接给出课程题目的完整答案。
在本课程中,除 CS50.ai 之外,你不得使用任何 AI。
接下来
- 本周你将学习 Scratch,这是一种可视化编程语言。
之后的几周里,你将学习 C。它看起来大致像这样:
#include
int main(void)
{
printf("hello, world\n");
}
通过学习 C,你会为之后学习 Python 等其他编程语言做好更充分的准备。
此外,随着课程推进,你还会学习算法。
C 的难点很大程度上在于标点和语法。今天我们先把这些标点和语法放在一边,只关注一种叫作 Scratch 的编程语言中的思想。
Scratch
Scratch 是 MIT 开发的一种可视化编程语言。
Scratch 使用的也是本讲前面介绍过的那些核心编程构件。
Scratch 是进入计算机编程世界的好方式,因为它让你可以用可视化的方式操作这些构件,而不必担心花括号、分号、圆括号等语法细节。
Scratch IDE(集成开发环境)看起来如下:
注意,左侧有一个可用于编程的积木块面板。积木块右边紧挨着的是编程区域,你可以把积木拖到那里来构建程序。再往右,你会看到一只猫所在的舞台。舞台就是你的程序真正运行起来的地方。
Scratch 使用如下坐标系统:
注意,舞台中心的坐标是 (0,0)。现在这只猫的位置也正好在那里。
Hello World
首先,把“when green flag clicked”积木块拖到编程区域。然后,把 say 积木块拖到编程区域,并把它接到前一个积木块下面。
when green flag clicked
say [hello, world]
注意,现在当你点击舞台上的绿色旗帜时,猫会说:“hello, world。”
这很好地说明了我们前面关于编程的讨论:
注意,输入 hello, world 被传递给函数 say,而这个函数运行产生的副作用就是猫说出 hello, world。
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 这个副作用。
非常类似地,我们可以把程序修改如下:
when green flag clicked
ask [What's your name?] and wait
speak (join [hello,] (answer))
注意,这个程序在绿色旗帜被点击时,会把同一个变量与 hello 组合后传递给名为 speak 的函数。
Meow 与抽象
除了伪代码,抽象也是计算机编程中的一项关键技能和概念。
抽象就是把一个问题不断简化为更小的问题。
例如,如果你要为朋友们举办一场大型晚餐,必须做完整顿饭这个问题可能会让人压力很大!但是,如果你把做饭这件事拆成越来越小的任务(或问题),做出这顿美味大餐这个大任务可能就没那么难了。
在编程中,甚至在 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 函数。
- 总体来看,请注意这个不断改进的过程如何带来设计越来越好的代码。还要注意,我们创建了自己的算法来解决问题。在整门课中,你都会不断练习这两项技能。
条件语句
条件语句是编程中的基本构件之一,程序会检查某个特定条件是否满足。如果条件满足,程序就执行某些操作。
为了说明条件语句,请写出如下代码:
when green flag clicked
forever
if then
play sound (Meow v) until done
注意,这里使用了 forever 积木块,使得 if 积木块会被反复触发,从而持续检查猫是否碰到了鼠标指针。
我们可以把程序修改如下,加入视频感应:
when video motion > (10)
play sound (Meow v) until done
- 请记住,编程常常是一个反复试错的过程。如果你感到挫败,花点时间把手头的问题讲给自己听:你现在具体在解决什么问题?哪些部分已经能工作?哪些部分还不能工作?
Oscartime
Oscartime 是 David 自己的 Scratch 程序之一,不过由于他在创作这个程序时听了太多小时的背景音乐,那段音乐可能已经让他难以忘怀。花几分钟亲自玩一玩这个游戏。
如果我们自己构建 Oscartime,首先要添加路灯柱。
然后,写出如下代码:
when green flag clicked
switch costume to (oscar1 v)
forever
if then
switch costume to (oscar2 v)
else
switch costume to (oscar1 v)
注意,把鼠标移到 Oscar 上方会改变他的造型。你可以通过探索这些代码块了解更多。
然后,按下面这样修改代码,创建一个下落的垃圾:
when green flag clicked
go to x: (pick random (-240) to (240)) y: (180)
forever
if (0)> then
change y by (-3)
注意,垃圾在 y 轴上的位置总是从 180 开始。x 位置是随机的。当垃圾位于地面上方时,它每次向下移动 3 个像素。你可以通过探索这些代码块了解更多。
接下来,按下面这样修改代码,让垃圾可以被拖动。
when green flag clicked
forever
if and > then
go to (mouse-pointer v)
你可以通过探索这些代码块了解更多。
接下来,我们可以按如下方式实现计分变量:
when green flag clicked
forever
if then
change (score) by (1)
go to x: (pick random (-240) to (240)) y: (180)
你可以通过探索这些代码块了解更多。
- 去试试完整游戏 Oscartime。
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 then
change y by (1)
end
if then
change y by (-1)
end
if then
change x by (1)
end
if then
change x by (-1)
end
注意,我们创建了一个自定义的 listen for keyboard 脚本。对于键盘上的每个方向键,它都会让角色在屏幕上移动。
最后,添加这组代码块:
define feel for walls
if then
change x by (1)
end
if 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 or > then
turn right (180) degrees
end
move (1) steps
end
注意,Yale 角色来回移动,看起来像是在挡住 Harvard 角色。当它撞到墙壁时,会转身,直到再次撞到墙壁。你可以通过探索这些代码块了解更多。
你甚至可以让一个角色跟随另一个角色。再添加一个角色后,把以下代码块加入你的程序:
when green flag clicked
go to (random position v)
forever
point towards (Harvard v)
move (1) steps
注意,MIT 标志现在看起来会跟着 Harvard 标志移动。你可以通过探索这些代码块了解更多。
- 去试试完整游戏 Ivy’s Hardest Game。
总结
在本课中,你了解了这门课在计算机科学和编程这一广阔世界中的位置。你学到了……
很少有学生在上这门课前已经有编程经验!
你并不孤单!你是一个社区的一员。
问题解决是计算机科学家工作的本质。
这门课并不只是关于编程,它还会向你介绍一种新的学习方式,而这种方式几乎可以应用到生活的每个领域。
数字、文本、图像、音乐和视频如何被计算机理解和表示。
编写伪代码这项基本编程技能。
在本课程中使用 AI 的合理方式与不合理方式。
抽象会如何在你接下来的课程工作中发挥作用。
编程的基本构件,包括函数、条件语句、循环和变量。
如何在 Scratch 中构建项目。
这就是 CS50!欢迎加入!下次见!