今天下午军训结束后,我去了图书馆,学习了大约一个小时。时间不算长,内容也没有铺得很开:Python 部分跟着黑马程序员的课程复习了 isinstance(),又看了 \n、\t 等写法;数学部分继续阅读《Understanding Analysis》,来到 induction,也就是数学归纳法这一小节。
这些内容以前都接触过,但今天的重点不是把“学过”再确认一遍,而是把过去比较粗略的印象重新拆开,看看每一种写法和每一步推理究竟在做什么。

Python:把“能运行”再说准确一点
isinstance(object, classinfo) 用来判断一个对象是否属于某个类型,返回值是 True 或 False。它也可以接收一个由多种类型组成的元组,用来判断对象是否属于其中任意一种类型。
score = 92
print(isinstance(score, int)) # True
print(isinstance(score, (int, float))) # True
我以前对类型的认识更多停留在 int、float、str 这些名称上。isinstance() 则把类型判断变成了一个可以直接运行和检验的条件。以后写分支判断或处理输入数据时,除了知道“这个变量看起来是什么”,还可以让程序明确检查它是什么类型。
这里需要分清一个小细节:\n、\t 不能都称为“换行语句”,更准确地说,它们属于字符串中的转义序列(escape sequences)。\n 表示换行,\t 表示水平制表符,二者并不完全相同:
message = "第一行\n\t第二行"
print(message)
输出时,第二行 会先换到下一行,再跳到下一个制表位置;具体显示宽度与输出环境有关。反斜杠在这里不是普通字符,而是告诉 Python:后面的字母需要按照一个特殊含义解释。概念本身不难,但如果只记住运行结果,而没有分清字符串、转义字符和实际输出之间的关系,之后遇到路径、引号或格式化文本时仍然容易混乱。
跟着课程重新看这些基础,也让我更具体地意识到之前学得比较粗:能够照着视频输入代码,只说明我完成了当时的操作,并不能说明已经形成了稳定理解。现在这一部分仍属于复习,接下来还需要离开示例,自己改动变量类型和字符串内容,观察判断结果与输出格式怎样变化。
数学归纳法:熟悉的方法,重新阅读它的逻辑
《Understanding Analysis》把归纳法表述为关于自然数集合的一条原则:如果一个集合包含 1,并且每当它包含某个自然数 n 时也包含 n + 1,那么这个集合就包含所有自然数。放到证明中,就是先验证起点,再说明一个命题一旦在第 n 步成立,就能够被推到第 n + 1 步。
它通常可以拆成三处需要写清的结构:
- 基础步骤(base case):先验证命题在起始值处成立;
- 归纳假设(inductive hypothesis):对任意的自然数
n,假设命题在n处成立; - 归纳步骤(inductive step):利用这个假设,推出命题在
n + 1处也成立。
书中的例子定义了一个递推数列:
x₁ = 1,并且xₙ₊₁ = (1/2)xₙ + 1。
要证明这个数列单调不减,可以先算出 x₂ = 3/2,于是 x₁ ≤ x₂,基础步骤成立。随后对任意自然数 n,假设 xₙ ≤ xₙ₊₁,把不等式两边同时乘以 1/2 再加上 1,便得到
xₙ₊₁ = (1/2)xₙ + 1 ≤ (1/2)xₙ₊₁ + 1 = xₙ₊₂。
这一步说明:如果第 n 项不大于第 n + 1 项,那么第 n + 1 项也不大于第 n + 2 项。起点和推进过程合在一起,才得到对所有自然数都成立的结论。

高中时已经学过数学归纳法,所以今天的计算并不陌生。不过从英文教材再读一遍,我更注意到证明中每一部分的职责:归纳假设不是把结论提前当成已经全部成立,而只是暂时允许使用第 n 步;真正需要完成的,是把这一有限的假设准确地推到下一步。现在我能够跟随书中的例子辨认结构,但是否能在新问题里独立组织证明,还需要通过习题检验。
今天留下的内容
一个小时里,我没有向前推进很多,但至少把两类基础内容重新说得更准确了:\n 与 \t 是功能不同的转义序列,isinstance() 提供了可执行的类型判断;数学归纳法则不只是“先验证、再假设”的固定句式,而是一条由起点不断向后传递的证明链。
下一次复习时,需要做的事情也比较明确:不用课程示例,自己写一段同时包含类型判断和转义字符的代码;合上教材,重新写出递推数列单调性的基础步骤、归纳假设与归纳步骤。用这两个小练习检验今天留下了多少内容,比只凭“看起来很熟悉”的感觉判断更可靠。
评论
0 条