Racket 编程入门

第 1 部分 · 起点:编程与计算的本质

写几行文字,电脑就会动

这是全书的第一章,不教你写任何一行 Racket。它只回答一个每个人都会问、却很少有人讲清楚的问题:为什么写几行文字,电脑就会动?

你正在读一本教编程的书。但在动手之前,有一件事比任何语法、任何工具都重要——你得先搞明白,编程到底是什么。

这不是废话。很多学编程的人,能背下一整本语法手册,却说不清自己每天在干什么。他们卡住的地方往往不是某个知识点太难,而是从一开始就没建立过一个基本的认知:凭什么我敲几行字,机器就照着做?这个困惑不解开,后面学什么都像在背咒语——能用,但不知道为什么能用。

这一章不讲 Racket,不讲任何具体语言。它只做一件事:把“编程”这件事拆到最朴素,让你带着一个清楚的心智模型走进后面所有的章节。

先问一个最基本的问题:电脑凭什么听话

你给朋友说“把桌上那三本书按厚薄排一下”,他会去做。你不用解释什么叫“厚薄”、什么叫“排序”、按什么方向摆——他靠常识就能补全所有你没说的细节。

电脑没有常识。

同样这句话,对电脑说,它每一个字都需要你拆开讲明白:怎么比较两本书的厚薄、谁和谁比、比完怎么交换位置、什么时候算排完。你脑子里的一个念头,到了电脑这里要变成一串精确到每一步的指令。

编程,就是把你脑子里的想法,翻译成电脑能照着做的指令。

这就是它最朴素的样子。没有更多神秘的东西。所谓“学编程”,学的就是怎么把一个模糊的想法,拆成一组明确到机器也能执行的步骤。电脑的价值不在于它聪明——它一点都不聪明,它只是快,而且绝不偷懒。你把步骤想对了,它替你瞬间做完;你哪步想错了,它就忠实地把错误执行到底。

理解了这一点,你就理解了为什么编程值得学:它逼着你把想法想清楚。一个你自以为懂了的东西,只有当你能把它拆成机器也能照做的步骤时,才算真懂了。

“翻译”靠的是符号和规则

人说话用自然语言,电脑听不懂。所以得有一套电脑能识别的东西来当翻译的媒介——这就是编程语言。

但不管哪种编程语言,它的核心都只有两样东西:

  • 符号:用来表示东西。数字 3 表示三,文字 "买牛奶" 表示一句话,一个名字 speed 表示某个值。
  • 规则:规定符号怎么组合、怎么变化。“3 加 2 等于 5”是一条规则,“按顺序执行这几步”也是一条规则。

就这样。编程语言再复杂,本质都是用一套符号加上一套规则,去描述你想要的计算过程。

编程,就是用符号表达规则,用规则产生结果。 记住这句话。后面你学的每一个语法、每一个特性,拆开看都是这两样东西的组合:要么是新的符号表示,要么是新的组合规则。当你不被表面的符号吓住,能看到底下的规则,你就摸到编程的门道了。

同一件事,为什么有那么多不同的写法

你很快会发现:同一件事,能用很多种完全不同的写法来做。

比如“算 1 加到 100”,有人这么想:从 1 开始,每次加下一个数,加到 100 停——这是一步一步改变某个累加值。也有人这么想:把 (1+100)、(2+99)…配成 50 对,每对都是 101,答案就是 50 × 101——这是用现成的数学公式一步算出来。

两种想法都对,思路却截然不同。编程范式,就是这些不同思路的归类。大家最常提的有三种:

  • 函数式:把计算看成数据的变换——输入进去,一路变换,输出出来,中间不改动任何东西。像流水线,东西从一头进、另一头出。
  • 命令式:把计算看成状态的改变——设立一些会变的量,一步一步地改它们,直到得到想要的结果。像给机器写操作手册。
  • 面向对象:把世界看成一个个对象,每个对象有自己的数据和能做的事,对象之间互相协作。像在模拟一个小世界。

这三种不是非此即彼的对头,而是看待同一个问题的三种角度。真实的程序,往往是几种思路混着用的。

这恰恰是 Racket 的特别之处——它把这三种思路都原生支持,你不用为了体会函数式去学 Haskell、为了体会面向对象去学 Java。在一门语言里,你就能看清这三种范式各自的长处和短处。这是这本书会带你走过的旅程:从函数式起手,到命令式管状态,再到面向对象搭界面,最后发现它们本来就是同一件事的几个面。

为什么世界分出了这么多“领域”

你一定听过这些词:Web 开发、GUI 编程、游戏开发、数据处理……它们听起来像是一门门独立的手艺,让人还没学就先发怵。

但它们其实都不是新东西,而是同一件事——用符号和规则描述计算——在不同场景下的变形。变形的原因,是不同场景对“状态”的处理方式不一样:

  • 写一个算式、做一次计算,输入给完就给完了,结果算出来就结束——这是最纯粹的,没有状态,给什么算什么。
  • 写一个红绿灯模拟,灯的颜色要随时间变——状态会随时间改变,你得管好这个变化。
  • 写一个带按钮的窗口程序,用户什么时候点、点什么你事先不知道——你得管一组持续存在、随时响应交互的对象。
  • 写一个网站,每次请求都是独立的、HTTP 协议本身不记你是谁——你得在一个没有记忆的协议上,硬造出“会话”的感觉。

你看,复杂度是一层层加上去的,但内核始终是那个“用符号和规则描述计算”。GUI 编程、Web 编程之所以存在,不是因为它们发明了新东西,而是因为现实问题需要不同的状态管理方式。理解了这条线,你就能看穿那些唬人的名词——它们都是同一棵树上长出的不同枝桠。

[架构图:状态复杂性的递进阶梯——每级是上一级的升级]

state-complexity-staircase

这本书的后半段,会带你沿着这条线走一遍:先学纯粹的计算,再加上状态,再加上交互,最后走到能上线的产品。每一步都建立在前一步上,没有任何跳跃。

所以,这本书会怎么带你走

到这儿,你已经有了这本书最需要的两个认知:

编程是把想法翻译成机器能照做的指令,靠的是符号和规则。不管后面遇到什么新概念,都可以回到这一句去理解它。

所有看似不同的领域,都是同一件事的变形。 你不用为每一个新名词重新发愁,它们都共享同一个内核。

基于这两个认知,这本书的设计是这样的:

  • 先回答“编程是什么、计算的本质是什么”——就是你正在读的这几章
  • 然后装好 Racket,学它的符号和规则——从括号、数据、函数开始
  • 接着深入函数式,这是 Racket 的核心,也是理解计算最深的路
  • 再到命令式、面向对象、GUI——状态一层层加上去
  • 最后是宏、延续、DSL——你会发现 Racket 不只是用别人造的语言,还能让你自己造语言
  • 一路走到工程化实战:Web、数据、打包部署,做出能用的产品

这是一条从“为什么写几行字电脑就会动”一直走到“用 Racket 做出一个产品”的完整路径。每一步都讲清楚为什么,不让你背任何一句咒语。

下一篇,我们先把那个最朴素的问题再往深里挖一步:电脑到底在干什么?这个问题,半个多世纪前有两个数学家给出了两个绝妙的回答——图灵机和 λ 演算。它们看起来和编程无关,却是今天你写的每一行代码的祖宗。