Skip to main content

CS 61A 2024春季学期 | 家庭作业 9

家庭作业 9:程序作为数据,宏

截止时间:4月25日周四晚11:59

说明事项

下载 hw09.zip。压缩包内包含名为 hw09.scm 的文件,以及 ok 自动评分工具。

提交: 完成后,请将所有已编辑的代码文件上传至 Gradescope。截止日期前可多次提交,最终提交版本将被评分。请务必检查在Gradescope上是否成功提交。更多提交说明请参考Lab 0

使用 Ok: 如有关于 Ok 的疑问,请参考本指南

参考资料: 以下资源可能对您有所帮助:

评分标准: 本次作业根据正确性评分,每错一题扣一分。总分 2 分。

每个 Scheme 作业都包含 61A Scheme 解释器。 启动方法:在终端输入 python3 scheme。 加载 f.scm 文件:输入 python3 scheme -i f.scm。 退出解释器:输入 (exit)

Scheme 编辑器

所有 Scheme 作业都提供一个网页编辑器,方便运行 ok 测试和可视化环境。在终端输入 python3 editor,编辑器将在浏览器窗口打开 (地址:http://127.0.0.1:31415/)。如需停止编辑器并返回命令行,请在启动编辑器的终端中按下 Ctrl-C

“运行”按钮会加载当前作业的 .scm 文件并启动 Scheme 解释器,方便您尝试运行不同的 Scheme 表达式。

“测试”按钮会运行所有 ok 测试。对于失败的测试,点击“查看案例”,然后点击“调试”进行单步调试。

推荐的 VS Code 扩展

如果您选择使用 VS Code 作为代码编辑器(而非网页编辑器),建议安装 vscode-scheme 扩展,以启用括号高亮显示。

之前:

之后:

此外,61a-bot VS Code 扩展(参见安装说明)也可用于 Scheme 作业。此工具已集成到 ok 中。

作业题目

新手入门视频

这些视频可能会为解决此作业中的编码问题提供一些有用的指导。

观看视频前,请确保您已登录 berkeley.edu 邮箱。

YouTube 链接

程序即数据:柯里化

回想一下,柯里化是指将一个接受多个参数的函数转换成一系列接受单一参数的高阶函数。请记住这一点。接下来的问题中,你将利用“程序即数据”的思想,创建能够自动柯里化任意长度函数的函数!

Q1: 实现柯里化函数

实现函数 curry-cook,它接受一个 Scheme 列表 formals 和一个带引号的表达式 bodycurry-cook 应该生成一个程序,该程序作为一个列表,是 lambda 函数的柯里化版本。输出的程序应为一个柯里化的 lambda 函数,其形式参数为 formals,函数体为 body。你可以假设所有传入的函数都至少有一个形式参数(formals);否则,无法进行柯里化。

例如,如果你想柯里化函数 (lambda (x y) (+ x y)),你可以将 formals 设置为 '(x y),将 body 设置为 '(+ x y),并调用 curry-cook(curry-cook '(x y) '(+ x y))

scm> (curry-cook '(a) 'a)
(lambda (a) a)
scm> (curry-cook '(x y) '(+ x y))
(lambda (x) (lambda (y) (+ x y)))
(define (curry-cook formals body)
'YOUR-CODE-HERE
)

使用 Ok 来测试你的代码:

python3 ok -q curry-cook

Q2: 应用柯里化函数

实现函数 curry-consume,它接受一个柯里化的 lambda 函数 curry,并将该函数应用于参数列表 args。你可以做出以下假设:

  1. 如果 curry 是一个经过 n 次柯里化的函数,那么 args 中最多可以包含 n 个参数。
  2. 如果参数数量为 0args 是一个空列表),则你可以假设 curry 已经完全应用了相关的参数;在这种情况下,curry 现在包含一个表示 lambda 函数输出的值。返回它。

请注意,对于相应的 lambda 函数 curryargs 的数量可能少于 formals! 如果提供的参数数量少于所需,curry-consume 应该返回一个柯里化的 lambda 函数,该函数是 curry 函数部分应用了已提供的参数后的结果。示例见下方 doctest。

scm> (define three-curry (lambda (x) (lambda (y) (lambda (z) (+ x (* y z)))) ))
three-curry
scm> (define eat-two (curry-consume three-curry '(1 2))) ; pass in only two arguments, return should be a one-arg lambda function!
eat-two
scm> eat-two
(lambda (z) (+ x (* y z)))
scm> (eat-two 3) ; pass in the last argument; 1 + (2 * 3)
7
scm> (curry-consume three-curry '(1 2 3)) ; all three arguments at once
7
(define (curry-consume curry args)
'YOUR-CODE-HERE
)

使用 Ok 来测试你的代码:

python3 ok -q curry-consume

Q3: 切换到 Cond

switch 是一个宏,它接受一个表达式 expr 和一个由数对 options 组成的列表。每个数对的第一个元素是一个值,第二个元素则是一个表达式。switch 会计算 options 列表中,与 expr 的计算结果相匹配的表达式。

scm> (switch (+ 1 1) ((1 (print 'a))
(2 (print 'b)) ; (print 'b) is evaluated because (+ 1 1) evaluates to 2
(3 (print 'c))))
b

switch 在其实现中使用另一个名为 switch-to-cond 的过程:

scm> (define-macro (switch expr options)
(switch-to-cond (list 'switch expr options))
)

你的任务是定义 switch-to-cond,这是一个过程(而非宏),它接受一个带引号的 switch 表达式,并将其转换为行为相同的 cond 表达式。 例如:

scm> (switch-to-cond `(switch (+ 1 1) ((1 2) (2 4) (3 6))))
(cond ((equal? (+ 1 1) 1) 2) ((equal? (+ 1 1) 2) 4) ((equal? (+ 1 1) 3) 6))
(define-macro (switch expr options) (switch-to-cond (list 'switch expr options)))

(define (switch-to-cond switch-expr)
(cons _________
(map
(lambda (option) (cons _______________ (cdr option)))
(car (cdr (cdr switch-expr))))))

使用 Ok 来测试你的代码:

python3 ok -q switch-to-cond

在本地检查你的分数

你可以通过运行以下命令,在本地查看本次作业中每个题目的得分情况:

python3 ok --score

这不会提交作业! 当你对得分感到满意时,请将作业提交到 Gradescope 以获取学分。

提交

通过将你编辑过的任何文件上传到相应的 Gradescope 作业来提交此作业。Lab 00 包含详细说明。

此外,所有不在大型实验室的学生都必须填写此考勤表。 每周提交此表格,无论你是否参加了实验课,或者因为正当理由错过了它。 大型实验室的学生不需要填写考勤表。

往年考题练习

作业还将包含以前的考试题供你尝试。 这些题目不计入成绩,欢迎尝试练习!

  1. 2019 年秋季期末考试 Q9:宏镜头
  2. 2019 年夏季期末考试 Q10c:切片
  3. 2019 年春季期末考试 Q8: