| 关于   英文版   «  第 1 章 Scratch   ::   目录   ::   2.1. 第 1 周:C  »

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. 计算机科学与问题解决

  • 本质上,计算机编程就是接收输入、产出输出——从而解决问题。输入与输出之间发生的、可称为 黑盒 的部分,正是本课程要探讨的重点。

    flowchart
  • 举例来说,我们需要统计班级出勤。可以用“一元计数法”(也叫 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(红色)、n/2(黄色)、log₂ n(绿色凹曲线)三条曲线的增长速度对比

    注意第一种算法(图中红色曲线)的大 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 界面

    注意左侧是一个 积木块 调色板,供你编程使用;紧挨积木块右侧,是可以把积木拖进去搭建程序的操作区;再往右则是小猫所站的 舞台——你的程序在此活起来。

  • Scratch 使用如下坐标系:

    Scratch 坐标系

    注意舞台中心是坐标 (0,0)。此刻小猫正好位于这个位置。

1.1.11. Hello World

  • 开始时,把“when green flag clicked”积木拖到编程区。再把 say 积木拖过去,接到上一块下面。

    when green flag clicked
    say [hello, world]
    

    注意点击舞台上的绿旗后,小猫就会说“hello, world”。

  • 这很好地印证了我们前面关于编程的讨论:

    带黑盒的 Scratch

    注意输入 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, 加上一个名字。你的程序现在有了交互。

  • 在整个课程中,你都会给算法提供输入、获得输出。就用上面这个程序,可以这样表示:

    作为算法的 Scratch

    注意输入 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,先加上路灯。

    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
    end
    
    define 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!欢迎登船,下次见!

   «  第 1 章 Scratch   ::   目录   ::   2.1. 第 1 周:C  »

关闭窗口