一、那一卷纸带:机器码(0/1)
老周把旧收银机拆开,指着里面一块布满锈迹的电路板:"这就是 1986 年那台机器的'大脑'。它什么都不认识,只认电信号——有电是 1,没电是 0。"
他从抽屉里拿出那卷纸带,展开给小陈看:
. . . . . . . . . . . . . . . . . . . . . . . . .
: . . : : . . : : . : : . . . : . . : . : . . :
. . . . . . . . . . . . . . . . . . . . . . . . .
"纸带上有孔就是 1,没孔就是 0。当年程序猿写程序,就是在这卷纸上打孔。比如让机器做加法,这一排孔的含义是:"
10110010 00000101 ← 把 5 装进寄存器
10110010 00000011 ← 把 3 装进寄存器
00000011 ← 执行加法
"这就是机器码。每一串 0/1 都是一个指令,机器照单执行,没有任何废话。"
小陈看着纸带,半天憋出一句:"……这哪是写程序,这是织毛衣啊。"
"对!所以人受不了了。"老周笑着说,"人记不住一长串 0 和 1,这是第一个麻烦。 为了解决它,我们发明了——"
二、助记符:汇编(MOV / ADD / JMP)
老周在纸上写下三行字:
MOV AX, 5 ; 把 5 放进 AX 寄存器
ADD AX, 3 ; 把 AX 加 3
INT 21h ; 调用系统中断,输出结果
"这叫汇编,把 0/1 换成了人类能勉强看懂的英文缩写:MOV(移动)、ADD(加)、JMP(跳转)。机器还是只认 0/1,但我们写的时候不用记那一串串数字了——有个叫'汇编器'的程序帮我们把助记符翻译成机器码。"
小陈试着读:"MOV AX, 5……嗯,比 10110010 好懂多了。"
"但你还是能感觉到,这离'人话'还很远。你看这一行:INT 21h——谁能想到这是'在屏幕上打印'?"老周顿了顿,"而且你注意到没有:代码里全是 5、3 这种裸数字,换个数就得整个重写。 如果书价是 29 块 9,我总不能每次打折都去改机器指令吧?"
"所以我们需要——"
三、给数据起名字:变量
老周打开现在收银机里跑的那套系统,敲了两行代码给小陈看:
price = 29.9 # 这本书的价格
quantity = 2 # 买了几本
total = price * quantity
print(total)
"变量,就是给数据起个名字。price 是价格,quantity 是数量,total 是总额。你不用记 29.9 存在内存哪个地址——你只管叫它 price,编译器帮你管地址。"
"这个名字起得好,代码就是自解释的。"小陈点头。
"而且变量还有更深的一层意义。"老周压低声音,"变量是编程史上第一次'抽象'——我们把'内存里的某个位置'这个又脏又累的细节,藏到了一个名字后面。从这一刻起,我们离硬件远了一步,离人脑近了一步。"
老周在白板上画下第一笔:
根:机器能执行什么?
├── 机器码(0/1) ← 机器认识,人记不住
└── 汇编(助记符) ← 人勉强认识,还是太硬核
└── 需要:给数据起名字 → 变量 ✅ ← 现在到这儿了
四、第一层:控制流
"现在,我们有了变量,能算账了。"老周把键盘推给小陈,"但书店的生意没那么简单。我给你出个真实需求,你试试用代码写出来。"
需求 1:顾客买书,如果总价超过 100 元,打 9 折;否则原价。 需求 2:结账时,要根据书的类型(小说/教材/漫画)走不同的折扣规则。 需求 3:下班前,要把今天卖出去的所有书都过一遍,统计总营业额。
小陈挠头:"这……需求 1 是'判断',需求 2 也是'判断',需求 3 是'重复'。这些我在学校都写过!"
"对,它们有个总名字,叫控制流(Control Flow)——控制程序'接下来往哪走'。"老周说,"没有控制流,程序就像一列火车,只能沿着一条轨道往前开。有了控制流,它才变成一张地图,能拐弯、能绕圈。咱们一个一个来。"
4.1 条件:if
小陈写:
total = 120.0
if total > 100: # 如果超过 100
total = total * 0.9 # 打 9 折
print("已打9折,实付", total)
else: # 否则
print("未达折扣门槛,实付", total)
"跑一下。"老周看着输出 已打9折,实付 108.0,"好,条件分支(if/else),就是程序走到岔路口,根据条件的真假选择一条路。注意:total > 100 这叫条件表达式,它的计算结果只有两种:真(True)或假(False)。"
"那需求 2 呢?"小陈问,"三种书三种折扣,if 写起来好长一串……"
4.2 多路分支:switch / match
老周说:"多分支的时候,if-else 写出来又长又丑,于是有了专门的语法——switch(很多语言)或 match(Python 3.10+ / Rust)。你看:"
book_type = "小说"
match book_type:
case "小说":
discount = 0.95 # 小说 95 折
case "教材":
discount = 0.90 # 教材 9 折
case "漫画":
discount = 0.88 # 漫画 88 折
case _: # 其他
discount = 1.0
print("折扣:", discount)
"看到 case _ 了吗?这是'兜底分支',谁都没匹配上就走它。场景:不同情况走不同分支——这就是 switch/match 存在的意义。"
"嗯,比一长串 if-else 清爽多了。"小陈点头。
4.3 循环:for / while
"最后是需求 3:盘点今天卖的每一本书。"老周说,"如果店里只有 3 本书,你可以复制粘贴 3 遍;可云间书店有 5 万本库存,你打算复制粘贴 5 万遍?"
小陈缩了缩脖子:"那不得写到明年……"
"所以有了循环。"老周示意他敲键盘:
sales = [29.9, 45.0, 12.5, 88.0, 23.0] # 今天卖出的书价列表
total_revenue = 0
for price in sales: # for 循环:依次取出每一本书的价格
total_revenue += price # 累加
print("今日营业额:", total_revenue) # 198.4
"for price in sales,意思是:把 sales 里的每个元素依次交给 price,执行一次循环体。这段代码不管列表里有 3 个元素还是 5 万个,写法都一样——重复执行,但不用复制粘贴,这就是循环的意义。"
"那 while 呢?"小陈追问。
"for 是'我明确知道要循环多少次';while 是'我不知道多少次,但知道什么条件下要继续'。"老周举了个例子,"比如收银台一直开着,直到顾客说'结账'——这就像 while not 顾客说结账: 继续等待。来,试试:"
# 模拟收银:顾客一直加购,直到说"结账"
order_total = 0
while True:
action = input("输入书价(结账请输入 q):")
if action == "q":
break # 跳出循环
order_total += float(action)
print("本单合计:", order_total)
"while True 是'永远循环',靠里面的 break 主动跳出去。注意,循环配了 break(跳出)和 continue(跳过本次继续下一次) 才完整。"
五、章末:老周的"第一层"总结
临下班,老周把徒弟叫到白板前,把第一章的地图补完整:
根:机器能执行什么?
纯文本
机器码(0/1)
└── 汇编(助记符:MOV/ADD/JMP)
└── 需要:给数据起名字 → 变量
第一层:控制流(让程序能做选择、能重复)
纯文本
变量
├── 顺序执行 ← 程序默认沿着代码一行行往下走
├── 条件(if/switch/match)
│ └── 场景:不同情况走不同分支
└── 循环(for/while/do-while)
└── 场景:重复执行,不想复制粘贴代码
"小陈,你记住三个为什么:"
- 机器码 → 汇编,是因为人记不住 0/1;
- 汇编 → 变量,是因为人不想记内存地址;
- 有了变量还要控制流,是因为真实生意里有'选择'和'重复',没有它们,程序就是一条道走到黑的傻火车。
"这套系统跑到这一步,收银机能算账、能打折、能盘点。但它有个大毛病——"老周指了指屏幕上那段越写越长的代码,"同一个'计算折扣'的逻辑,我在三个地方各写了一遍。 你说,明天咱们该干嘛?"
小陈眼睛一亮:"把它抽出来?"
"对。那是第二章的事——函数。明天见。"