跳转至

Week12-Lecture27-29

Lecture 27 Scheme Lists

Lab 08 Scheme

这个语法真的有点看的🤢,继续往下了。

其实主要作用是了解里面的基本语法,之后能用python实现它的解释器就OK了。

Lecture 28 Calculator

Reading 3.3 Exceptions

抛出异常(Raising)

  • 异常本质上是一个对象,其类继承自 BaseException。
  • assert 语句抛出的是 AssertionError。
  • 更常见的做法是用 raise 语句主动抛出一个异常实例,例如 raise Exception('An error occurred')。
  • 一旦异常被抛出且没被处理,当前代码块后续语句都不会执行,解释器会打印出调用栈信息(stack backtrace)后返回交互环境或终止程序。
Python
1
2
3
4
try:
    <try suite>
except <exception class> as <name>:
    <except suite>

3.3.1 异常对象

异常对象本身可以携带额外信息(属性),比如出错时的上下文数据。用户可以自定义异常类来实现这一点。

牛顿法例子:

  1. 定义一个继承自 Exception 的新异常类 IterImproveError,它在初始化时保存"最近一次的猜测值" last_guess。
  2. 修改 improve 函数,在其内部捕获可能出现的 ValueError(比如对负数开平方触发的 math domain error),然后包装成 IterImproveError 抛出,同时把当前猜测值一并带出。
  3. find_zero 函数再捕获这个 IterImproveError,直接返回其中保存的 last_guess,而不是让程序崩溃。

这样一来,即使牛顿法在迭代过程中因为浮点误差跑到定义域之外导致报错,程序也能返回一个"虽不精确但可用"的近似解,而不是直接抛错终止。

核心思想:异常机制让我们可以把"正常的迭代改进逻辑"和"错误处理逻辑"分离到不同代码块中,是程序模块化的又一种体现,后面实现解释器时也会用到这个技巧。

Reading 3.4 Interpreters for Languages with Combination

这一节讲的是如何用 Python 实现一个简单语言的解释器——这是"元语言抽象"(用一种语言去实现另一种语言)思想的具体体现。

3.4.1 A Scheme-Syntax Calculator

Calculator,只支持四则运算 + - * /。它沿用 Scheme 的调用表达式语法:

  • + 和 * 可以接受任意数量的参数(0 个也行,分别返回单位元 0 和 1)
  • - 和 / 比较特殊:只传一个参数时表示取负/取倒数;传多个参数时表示第一个减去(除以)其余的和(积)

3.4.2 Expression Trees

要写解释器,就得把表达式当作数据来操作,而不只是概念上的树。这里用 Pair(类似 Scheme 的 cons pair)和空表 nil 来表示嵌套列表,Calculator 里所有的调用表达式本质上都是嵌套的 Scheme 列表,比如 (+ (* 3 4) 5) 会被表示成嵌套的 Pair 对象。

3.4.3 Parsing Expressions

解析分两步:

  1. 词法分析(tokenize):把输入字符串切分成最小语法单元(token),比如括号、符号、数字。tokenize_line 函数负责这一步。
  2. 语法分析(syntactic analysis):把 token 序列递归地组装成表达式树,由 scheme_read 和辅助函数 read_tail 完成。因为一个表达式常常嵌套包含子表达式,所以这个过程是树递归的。

解析出错时会抛出带有描述信息的 SyntaxError。

3.4.4 Calculator Evaluation

核心是两个函数:

  • calc_eval(exp):对表达式求值。如果是数字,直接返回(这叫"自求值",self-evaluating);如果是 Pair(调用表达式),先递归求出所有操作数的值,再调用 calc_apply。
  • calc_apply(operator, args):根据运算符字符串,用 reduce 等方式把对应的算术操作应用到参数列表上,并对参数个数不对的情况抛出 TypeError。

这体现了经典的"按类型分派"(dispatch on type)结构:表达式的形式(数字 vs. 调用表达式)决定了求值方式。

REPL(读取-求值-打印循环):不断读取输入、解析、求值、打印结果,并通过 try/except 捕获 SyntaxError、TypeError、ZeroDivisionError 等异常,让用户输入错误后程序不会崩溃、还能继续交互,还支持 Ctrl-C / Ctrl-D 退出。

Project4: scheme