2.9
TS 版
3-1
define-syntax
这个非常像 Mathematica 的模式匹配。结构化表达式,其第一个元素是下划线(_)
mma
但是更像过程宏。
update: 在读完了一些书后理解了, 这个就是在 bool 上面定义的范畴.
3-2
很可能会遇到一个提示告诉你
sum
未定义。这是因为变量 sum
仅仅在 let
表达式的 body 中可见,而 lambda
表达式并不是 body 的一部分,我们可以把 sum
作为参数传递给它自己,来绕过这个限制。一句话:let 的局部匿名函数没有名字,没法在自己里面 call 自己。
mma 的
#0
是自己,cpp 用 auto f = [](auto &&f, ....) {};
下面是 lua 的 2 个版本
let 自递归写法
TS
3-3
call/cc
call/cc 是 break + 可选的返回。类似一个存档点。
3-4 CPS
就是回调。
草,突然意识到 Scheme 没有 return/break, 所以要用 cps/callcc 来实现。
3-5 补全翻译
在第 2.6 节中,我们讨论了顶层定义。定义也可以出现在 lambda、let 或 letrec 主体的前面,在这种情况下,它们创建的绑定是主体的局部。
由内部定义约束的程序可以是相互递归的,就像letrec一样。例如,我们可以用内部定义重写第3.2节中的偶数和奇数的例子,如下所示。
同样地,我们可以在列表的第一个定义中用 race 的内部定义取代使用letrec来绑定 race。
事实上,内部变量定义和 letrec 实际上是可以互换的。除了语法上的明显区别外,唯一的区别是变量定义保证从左到右被计算,而 letrec 的绑定可以以任何顺序被计算。所以我们不能完全用 letrec 表达式来替代包含内部定义的 lambda 、let 或 letrec 主体。然而,我们可以使用 letrec*,它和 let* 一样,保证从左到右的计算顺序。一个形式为
相当于一个 letrec* 表达式,在包含表达式的主体中,将定义的变量与相关的值结合起来。
反过来说,一个长成下面样子的 letrec*
可以用一个包含内部定义和主体表达式的 let 表达式来代替,如下所示。
这些转换之间似乎缺乏对称性,这是因为 letrec* 表达式可以出现在表达式有效的任何地方,而内部定义只能出现在主体的前面。因此,在用内部定义替换 letrec* 时,我们一般必须引入一个let表达式来容纳这些定义。
内部定义和 letrec 或 letrec* 的另一个区别是,语法定义可以出现在内部定义中,而 letrec 和 letrec* 只绑定变量。
由内部语法定义建立的语法扩展的范围,与内部变量定义一样,仅限于语法定义出现的主体。
内部定义可以与顶层定义和赋值一起使用,以帮助程序模块化。程序中的每个模块应该只显示其他模块需要的绑定,而隐藏其他绑定,否则会使顶层命名空间变得混乱,并可能导致对这些绑定的非预期使用或重新定义。一个常见的模块结构方式如下所示。
第一组定义为我们希望导出的变量建立顶层绑定(使其在全球范围内可见)。第二组定义建立了仅在模块内可见的局部绑定。表达式 init-expr ... 执行任何在建立本地绑定后必须进行的初始化。最后,这组表达式将导出的变量分配到适当的值。
这种模块化形式的好处是,在程序开发过程中可以删除或 "注释 "括号中的let表达式,使内部定义成为顶层定义,以方便交互式测试。这种形式的模块化也有几个缺点,我们将在下一节讨论。
下面的模块导出了一个单一的变量,calc,它被绑定到一个实现简单的四函数计算器的过程中。
PY
遍历树
这个例子使用 case 表达式来决定应用哪个运算符。case 与 cond 相似,只是测试的内容总是一样的:(memv val (key ...)),其中 val 是第一个 case 子表的值,(key ...)是每个 case 子句前面的项目列表。上面例子中的 case 表达式可以用 cond 重写如下。
3-6
在上一节的末尾,我们讨论了一种模块化的形式,即从 let 内部分配一组顶层变量,同时将未发布的助手保持在 let 的本地。这种形式的模块化有几个缺点。
- 它是不可移植的,因为交互式顶层的行为甚至是存在并不被Revised报告所保证。
- 它需要赋值,这使得代码显得有些笨拙,并可能抑制编译器的分析和优化。
- 它不支持关键字绑定的发布,因为没有类似于关键字的设置!。
一个不存在这些缺点的替代方法是创建一个库。一个库输出一组标识符,每个标识符都是在库中定义的,或者是从其他库中导入的。一个导出的标识符不需要被绑定为一个变量;它可以被绑定为一个关键字。
下面这个库导出了两个标识符:变量 gpa->grade 和关键字 gpa。变量 gpa->grade 被绑定到一个过程中,该过程接收一个以数字表示的平均成绩(GPA),并根据四分制返回相应的字母等级。关键字 gpa 命名了一个语法扩展,其子形式必须都是字母等级,其值是由这些字母等级计算出来的GPA。
这个库的名字是(成绩)。这可能看起来是一种有趣的名字,但所有的库名都是括号内的。该库从标准(rnrs)库中导入,它包含了我们在本章和上一章中使用的大部分基元和关键字绑定,以及我们实现gpa->grade和gpa所需要的一切。
除了 gpa->grade 和 gpa 之外,库中还定义了其他几个语法扩展和程序,但其他的都没有被导出。那些没有被导出的程序只是那些被导出的程序的助手。在库中使用的一切都应该是熟悉的,除了apply程序,它在第107页有描述。
如果你的Scheme实现支持在交互式顶层导入,你可以测试这两个导出,如下所示。
第10章更详细地描述了库,并提供了更多的使用实例。