十点四十
晚上十点四十。最后一本作文合上,红笔帽扣回去,右手中指侧面有一道浅浅的印子。
白天教研组长说了一句话:这次月考,全年级作文里“的、地、得”用错的比例,高得离谱。
你想知道,到底有多离谱。
于是你把五十本作文重新摊开,从第一本开始,一行一行看。看到“慢慢地走过”记一笔,看到“飞快的跑”再记一笔。四十分钟后,你得到一个大概的数字,和一双酸胀的眼睛。
而且你并不太确定它对——数到第三十一本的时候,你接了个家长的电话。
这个晚上你做的事,叫人工统计。它的问题是:慢,累,而且不可靠。
但请你回头看一遍,刚才那四十分钟,你做的事情是什么形状的:
你心里有一条规则(“地”后面多半跟动词,“的”后面多半跟名词——大概如此);你把这条规则,应用到一篇又一篇作文上;你在心里累加结果。
这个形状,就是程序。
机器能替你做的,只有一件事
不是思考,不是理解,不是“懂”。
是重复。
它不懂“乡愁”是什么。但它可以不知疲倦地数一数,这五十篇作文里“乡”字出现了多少次。一次不漏,一次不烦,数到第五十篇的时候和数第一篇的时候一样清醒。
“不知疲倦”这四个字,在人类这里是稀缺品,在机器那里是出厂设置。
所以编程的本质,不是让机器变聪明。是把你脑子里那条规则,翻译成它能执行的话。
而翻译,是要讲条件的。
它是个不猜的读者
你这些年练出来最厉害的本事之一,是听出学生没说出口的话。
一个男生站起来念“寒蝉凄切,对长亭晚”,念到“晚”字声音发虚,你就知道他昨晚没睡好,或者他没背。一个女生交上来的作文,题目写“我的父亲”,第一段写了三百字她家楼下的梧桐树,你就知道她父亲大概不在家。
这种能力叫读言外之意。它是语文老师的职业本能,也是一个人让人觉得温暖的根本原因。
现在,请你把它暂时关掉。
因为你要面对的这个读者,一点言外之意都读不出来。而且它不觉得尴尬,不觉得你在暗示,不觉得自己应该体贴你。你说什么,它就是什么。
举个你会笑的例子。
你对课代表说:“把这周的作文收上来,放我桌上。”
课代表会自己解决一系列你根本没说的细节:哪一周,当然是这周,周一到周五;哪些作文,当然是这周布置的那一篇;收上来按什么顺序放,他大概会按小组;如果有人没交怎么办,他会跟你说。
计算机不会。它会停在第一个问题那里,然后告诉你:你没有告诉我要收哪一种。
这就是它全部的性格。它不笨,它只是不猜。
于是有了三条规矩。听上去像写作课的要求,其实是编程的全部纪律。
一、话要一句一句说。
机器从上往下读,读完一行做一行,不跳读,不回头。你不能像写文章那样先埋一个伏笔,到最后一段再收。你要按它将要发生的顺序,一件一件说。
二、一个词只能有一个意思。
你写“稍微缩短一点”,学生知道是删掉两句话。机器不知道“稍微”是多少。你必须说:删掉最后 20 个字,或者删掉第三个句号之后的所有内容。
你写“大概三十篇”,机器会问你三十篇是哪三十篇。
在这门语言里,没有“稍微”,没有“大概”,没有“那个”。模糊不是风格,模糊是故障。
三、你没说的,它不会替你想。
文章的美,一半在留白。钱起写“曲终人不见,江上数峰青”,一曲终了,人散了,他只给你五个字:江上数峰青。剩下的全是白的,白得让人心里一空。
程序恰恰相反。留白的地方,它不会替你想,它会停在那里,或者更糟——它按自己最笨的理解往下走。
所以,学编程不是学一门新技术。
是学一种新的说话方式:把话说尽。
以后你会常常想起这个说法。当你写下一行代码,发现它跑出来一个荒唐的结果,多半是因为你在某个地方留了一片白,而机器老老实实地从这片白里走了过去。
第一句话
现在动手。安装 Python 的步骤在附录 A,跟着做就行,不用理解。
(如果你今天实在不想装,可以先搜一个“在线运行 Python”的网页试手。但最终还是要装到自己电脑上——那是你的厨房,别老去别人的厨房做饭。)
打开编辑器,敲下这一行:
print("同学们,把书翻到第四十七页。")
运行。
屏幕上出现这句话。
就这么简单,但值得停三秒:刚才有一个东西,按照你的意思,说了一句你的话。
print 这个词,字面意思是“打印”,但你可以把它理解成“说出来给人看”。它后面跟着一对括号,括号里是你要说的内容。内容要用引号包起来。
引号在这里的作用,和它在文章里的作用其实是一样的:引号里的东西是被引用的,不是作者在说话。 没有引号,机器会以为你在给它下命令;有了引号,它才知道这只是一串字。
现在你会遇到今晚的第一个坑,而且几乎一定会遇到:
中文引号。
Python 只认 " 这种直的英文引号,不认 “” 这种弯的中文引号。你的输入法默认是中文,所以你打出来的第一个引号,多半是错的。
然后你会看到一个红色的 SyntaxError,一堆英文,看起来像在骂你。
这不是你笨。这是所有中文使用者学编程的第一课,无一例外。你只是第一次遇到“中文和英文的标点在机器眼里是两个东西”这件事。
把它记下来。附录 B 里,这条排在第一位。
给万物一个称呼
第二件事:给东西起名字。
这件事你太熟了。同一句话里,一个人可以是“先帝”,可以是“陛下”,可以是“刘备”,可以是“昭烈帝”。为什么换着叫?因为场合不同,关系不同,说话的人站的位置不同。
名字是给人用的,不是给东西用的。
编程里的变量,就是给一个东西起一个代号。起了之后,你不必每次都把东西本身搬出来,你说代号,它就知道是谁。
line = "床前明月光"
print(line)
print(line)
print(line)
三行诗。
注意那个等号。它在编程里不念“等于”,念“赋值”——把右边的东西,放进左边的名字里。念“等于”你会糊涂,因为 a = a + 1 这句话,在数学里是错的,在编程里天经地义:把 a 加上 1,再放回 a 里去。
现在,一个问题。
如果要一百遍呢?
你大概会想:复制粘贴一百行。可以,能跑。
但假设写完你忽然发现,“床前明月光”应该改成“床前看月光”——这是《静夜思》在更早版本里的样子,一字之差,整首诗的意味就变了。你要改一百次。
凡是要改一百次的地方,就一定有更好的写法。
那个更好的写法叫“循环”,在第四章。
今天你只需要记住这种烦。真的烦一下,比听我讲十句都有用。这本书里所有重要的东西,我都会先让你烦一次,再给你。
第一次真正有用
好,现在做一件你会真的用上的事。
数字数。
高中作文要求不少于 800 字,判卷的时候按格子数,标点占一格。你数了太多年了。
zuowen = "把你的作文原文粘贴到这里"
print(len(zuowen))
len 是 length 的缩写。它数这段文字有多少个字符。
跑一下,看看那个数字。
然后你会发现一件很有意思的事:它数出来的,差不多就是你数格子的结果。 因为标点在机器眼里也是一个字符,和汉字一样,各占一个位置。
这个巧合会让你高兴一下——它数的,正好是你想数的那个东西。
但只高兴一下。
因为空格和换行也算。你从 Word 里复制出来的一段文字,末尾常常带着换行,中间可能还有多余的空格,这些都会被算进去。所以你数出来 812,格子上可能是 809。
这就是你第一次遇到“机器太老实”的麻烦。
请把这句话记住,它会陪你走完整本书:
它给的答案,永远是你问的那个问题的答案,而不是你想问的那个问题的答案。
你以为你在问“这篇作文够不够 800 字”,它听到的是“这串字符有多少个”。这两个问题在 99% 的时候答案一样,在剩下的 1% 里,会让你困惑一整个晚上。
程序员的功夫,一大半花在把“我想问的”翻译成“我该问的”。
数一个字的次数
最后一个动作,今晚就收工。
你想知道这五十篇作文里,“然后”用了多少次。上次你在课堂上说过一次,学生笑了,但笑完照旧。
print(zuowen.count("然后"))
count 是“数”的意思。zuowen.count(“然后”),读作:在这段叫 zuowen 的文字里,数一数“然后”出现了几次。
换成“的”,换成“其实”,换成“我觉得”,换成任何一个你想抓的口头禅。
一个数字出来了。
如果这个数字是 43,你可以明天把它写在黑板上,什么都不用说。学生会自己脸红。
程序不必伟大,只要今天有用。
这是我对整本书唯一的野心:你每学一章,当天就能拿一样东西回教室。
(顺便说一句,count 后面为什么带个点,为什么长得像 zuowen.count 而不是 count(zuowen)——第三章会讲。今天你先用,能用就行。这本书的一个原则是:先用,后懂。 就像学生先会背“床前明月光”,两年后才懂什么叫思乡。)
一句题外话:学编程最像学文言文的地方
一开始,每个字都认识,连起来不懂。
你逐字翻译,翻得磕磕绊绊,还要查注释。你甚至怀疑自己是不是不适合学这个。
但是读得多了,某一天,一整句忽然自己就通了。你说不清是哪天通的,也说不出通的那个瞬间发生了什么。
编程也是这样。
前两周,你会觉得每个符号都眼熟,每一行都要查。然后某一天,你看着一段完全陌生的代码,忽然知道它在干什么。
那一天会来。但它不会提前通知你,所以你不能用“我还没通”来判断自己该不该继续。
还有一件事,我要在这里说清楚,因为你在翻开这本书之前,大概最担心的就是这个:
这一章,你见到数学了吗?
没有。一个都没有。
整本书里,你需要的数学能力是:会比较大小,会数数,知道什么叫“一半”。就这三样。
你之所以觉得自己“数学不好所以学不会编程”,是因为你见过的所有编程材料,都是写给喜欢数学的人看的。它们用数列讲循环,用素数讲判断,用斐波那契讲递归——不是因为这些例子更好,是因为写书的人自己喜欢。
这本书用作文、诗词、病句、成绩单讲。不是因为它更简单,是因为它更像你。
卡住了怎么办
三个错误,今晚你至少会遇到一个。
一、SyntaxError,一串英文,看不懂。九成是标点。中文的引号、括号、逗号、冒号,机器一个都不认。把输入法切到英文,重新把那一行的标点打一遍。
二、括号没配对。print(“你好” 少了一个右括号。数一数,左括号和右括号必须一样多。
三、少了一个引号,后面全乱。机器会顺着你的引号一路找下去,把后面几行都当成“字”,然后报一个看起来完全不相关位置的错。
处理原则有两条,都很重要:
报错信息,从最后一行往上看。 最后一行告诉你是什么病,上面几行告诉你病在哪。就像改病句,先看它说“成分残缺”,再找缺的是哪个成分。
卡住二十分钟,就站起来。 去倒杯水,看看窗外。这个问题不会因为你盯着它而变简单,但你的脑子会因为走开而换个角度。这不是放弃,这是方法。
还有一句:把报错信息复制下来去搜索,是专业做法,不是作弊。 全世界每一个程序员,每天都要搜索十几次报错信息。包括那些你以为什么都懂的人。
本章你多了什么本事
三句话,都是人话:
你能让计算机,说一句你想说的话。
你能给一段文字起个名字,然后反复用它。
你能数出一段文字有多少个字,某个字出现了多少次。
明天可以拿去办公室说的一句话
“我昨天写了个程序,把我们班五十篇作文里'然后'数了一遍,平均一篇 11 次。”
注意这句话的杀伤力在哪里:不在“我在学编程”,在那个具体的数字。
下一章
这一章,机器只会“说”。
下一章开始,它会开始判断——它会区分:这个字是不是“的”,这篇作文够不够 800 字,这个学生到底交没交。
那是“如果”的登场。而“如果”,是你最熟悉的句式。