以下是 UCB CS61A 24fall 中 Scheme Project 各个 Problem 中我的答案以及对应的笔记和注意事项

Problem 1

这里是完成两个方法:define 和 lookup
define 其实和前面的 hw 差不多,直接返回对应的 dict 中的值即可
lookup 却要注意一点,这里的 env 存在继承关系,因此此 env 找不到的时候需要找找他的 parent env,看看有没有对应的 symbol

Problem 2

scheme apply 这个函数顾名思义,就是用来将 procedure(我的理解其实有点像某种运算),作用在 args 上,其中 args 是一个 scheme list
做完这个最大的感受是认真阅读网页的要求
比如给的提示:利用* 来传入未知数量的参数、把环境添加为list的最后一个元素
这里在做的时候一定要和ok里的一些例子、例题进行对照比较
同时需要善用编译器,找到对应的类具体是怎么回事

Problem 3

这里实现了一个 scheme eval 的函数,主要就是根据当前的 env 来给出对应位置的元素在 scheme 中应该对应的值:比如符号对应某个运算,数对应自己等等
其中,根据注释也可以看出,对于 atoms,也就是单个的 expr,就会查找是否是某个“关键字”,并且返回对应的操作
在 else 中,我们把 first 进行 eval,作为我们的 procedure
同时,由于 args 是一个 list,然而 rest 是一个 pair 类型的表,因此需要通过 map 函数进行转换,其中每一项都需要经过 scheme eval 来转换
最后再 apply 一下即可

Problem 4

这里是实现 define 的功能,需要参考在 Problem 1 中实现的 Frame类,也就是 env 所在的类的 define 方法,可以回去看一下
这里已经可以看到确定好了表达式地长度就是 2,也就是Pair("x", Pair("(+ 1 2)", nil))大概长这样
已经把 signature 提取出来,用于绑定,剩下需要我们去求后面表达式的值,需要注意:
  • expressions.first 就是 "x"
  • expressions.rest 是 Pair("(+ 1 2)", nil)
  • expressions.rest.first 才是真正要计算的那个表达式 "(+ 1 2)"
不要忽略这个 nil(我就 debug 了半天)

Problem 5

没难度,直接返回 first 即可

Problem 6

Problem 7

其实也比较简单,好好做 ok 部分就大概能摸清楚门路
formals 部分也就是 lambda 的参数部分
body 是 lambda 具体的函数运算
最后查看一下 lambdaprocedure 这个类,把对应的值传入即可

Problem 8

这个其实也比较简单,也是一遍过的,也或许是因为我找到感觉了,没有遗忘
这里已经帮着判定好了 len 相等
首先创建一个 child frame,用于往里面绑定,然后返回
由于 len 相等,用其中一个做遍历条件即可
然后进行遍历,通过 define 方法将 formal 与 value 绑定到 child frame 之中
最后返回

Problem 9

这个我卡了蛮久的,在 copilot 的提示下勉强理解
由于是隔天再写的,需要重新明确一下各个变量的概念
formals 是 lambdaprocedure 中参数的部分,是一个 list
vals 是 这里 apply 函数中传入的 args,是一串“数”,需要将 procedure 作用在上面
然后建立一个 child frame,把相关参数传入,实现在新的 env 下绑定
最后进行 eval all,其中,这里的 expressions 是 body 部分,也就是 lambda 中实际的函数部分
大概这样吧,我觉得差不多讲明白了

Problem 10

这里是要改进一下 define 这个方法
首先,要取出 function name
然后,需要把 signature 剩余部分和 body 部分抽离出来,并且使用 pair 将他们组合成新的 exp,用于传入 do lambda form 之中
接着创建一个 procedure 的实例,这里的 env 是按引用传递的(这个概念我卡了一会),在后面直接对 env 进行 define 方法,即可对 procedure 的 env 进行添加新的绑定

Problem 11

这个其实跟前面的很像,基本一样
这里照着Problem 9 做即可,需要注意的是,这里的 parent frame 应该是 env,而非 procedure.env
蛮简单的

Problem 12

这里的 and 和 or 差不多,改一下判定的 true 和 false,以及 val 的初始值就行
这里的 val 初始值是为了应对无参数的情况

Problem 13

这里是做一个 cond 的判断,如果 test 为真值(不一定非得是 true,如果返回值是数之类的也行),则运行以下部分
这里需要注意,如果返回值不是 true 但是为真值,且没有执行部分,则直接返回对应的值
这一步需要在 eval_all 这一步之前运行,不然 eval 之后test 的值似乎就变成 true 或者 false 了? 反正我在这里折腾了半天

后续:test.scm

如果按照上述代码的话,会有一行过不了测试,是关于 print 语句的,因此这里稍作修改,直接返回 eval 后的 value

Problem 14

这里也卡了一会,或许是因为过了几个小时回来再做的
首先,既然 hint 提到了两个 validate 方法,就一定要想想哪里需要用到
首先,这个函数接收一个 bindings,这是 pair 的形式,每个 name 和 value 绑在一块
现在需要通过遍历 bindings 来分别归到 names 和 vals 的 pair 之中,以便后面创建 child frame
需要注意的是:
  1. 需要检查 let 的 arg 数量,利用 validate form 实现,并且每次遍历都需要检查,因此放在循环之中
  1. 需要检查是否有重复引用,这里检查 names 是否有重复即可,在遍历完后,通过validate formals 实现
  1. 这里的 vals 添加的新 value 需要通过 eval 进行求值,而非直接加进去
  1. 这里eval 的一定是 first(如果是要取值,一定是 first,不然取到的是一个 pair 或者 nil)

Problem 15

来到了 scheme 部分,这里要写一个函数,可以返回 value 以及对应的 index,以一个 list 的形式
其实我对 scheme 部分忘了不少,问了 ai 不少问题才磕磕绊绊做出来,倒是都能明白,就是自己写想不出来,哎
首先,还是构造一个 helper 函数,用于辅助递归,有两个参数,列表 s 和 index
然后这里需要通过 cons 把每个(index,value)拼起来,内部其实用 list 或者 cons 都行? 具体我忘了(似乎 cons 是要考虑 nil?以及层级关系?)这里我用的 list,比较方便
最后进入递归进入下一次,更新 s 和 index
最后执行 helper 函数即可
下一个 Problem 尝试一下自己做,毕竟最后一个了

Problem 16

还好,基本算是写出来了,不过这里 cons 和 list 确实有些混淆,让 ai 讲了讲
这里其实感觉 hw 里做过类似的,不算太难,甚至都不需要 helper
来说一说这里为什么选择 cons 而非 list
💡
  1. list 与 cons 的区别:
  • list 创建新列表:(list 1 2 3) -> (1 2 3)
  • cons 添加单个元素:(cons 1 '(2 3)) -> (1 2 3)
  1. 在 merge 中:
  • 需要把一个元素添加到已有列表的头部
  • list 会创建新的嵌套列表,导致错误的结构
 
至此,据说是最难的 Scheme Project 全部完成!(EC 先放放?)
Loading...
昊卿
昊卿
一个普通的干饭人🍚
最新发布
大一上学期总结
2025-3-9
4.1 多层感知机
2025-3-7
3.4 softmax 回归
2025-3-5
3.3 线性回归的简洁实现
2025-3-5
3.2 线性回归的从零开始实现
2025-3-5
3.1 线性回归
2025-3-5