讨论 9 | CS 61A 2024 年春季学期
讨论 9:Scheme,Scheme 列表和引用 (Quotation)
注意: 我们仍然会使用 Pensieve,但是我们已经从 Pensieve 中移除了语音/视频聊天功能。请用 Discord 联系课程人员。Discord 更稳定,而且支持屏幕分享。随时在 Discord 的 #discuss-queue
频道 @discuss 提问,课程人员会加入你们的语音频道。
小组里派个人[加入 Discord]就行。当然,多几个人加入也行,一个就够了。
接下来,请切换到 Pensieve:
- 大家: 前往 discuss.pensieve.co 并使用您的 @berkeley.edu 电子邮件登录,然后输入您的小组号码。(您的小组号码是您的 Discord 频道号码。)
进了 Pensieve 之后,就不用再回到这个页面了;Pensieve 上的内容和这里一样(而且功能更多)。如果 Pensieve 出了什么问题,请返回此页面并继续讨论。
有问题就去 Discord 的 #help
频道求助。
小贴士: 你们可以在小组的 Discord 频道文本框里 @discuss 提问,课程人员会回复的。
开始之前
如果你们组只有一两个人,可以跟其他组一块儿讨论。
大家先自我介绍一下,然后看看谁最近摸过狗。欢迎分享狗的照片,猫片也行。
Scheme
Q1:完美平方
定义: 完全平方数就是 k*k
(k 为整数)。
写一个函数 fit(total, n)
,输入两个非负整数 total
和 n
,判断是否存在 n
个不同的正完全平方数,加起来等于 total
。
注意: 别用 Scheme 解释器验证答案!好好讨论!期末考试可没得用。
; 判断是否存在 n 个互不相同的完全平方数,加起来等于 total
(define (fit total n)
(define (f total n k)
(if (and (= n 0) (= total 0))
#t
(if (< total (* k k))
#f
'YOUR-CODE-HERE
)))
(f total n 1))
(expect (fit 10 2) #t) ; 1*1 + 3*3
(expect (fit 9 1) #t) ; 3*3
(expect (fit 9 2) #f) ;
(expect (fit 9 3) #f) ; 1*1 + 2*2 + 2*2 因为重复的 2*2 所以不算
(expect (fit 25 1) #t) ; 5*5
(expect (fit 25 2) #t) ; 3*3 + 4*4
在 61A 代码平台运行
提示:用 (or _ _)
组合两个递归调用:一个用 k*k
,另一个不用。用 k*k
的那个,total
减 k*k
,n
减 1;不用 k*k
的那个,total
和 n
保持不变。两种情况都要把 k
加 1。
展示时间: 小组讨论一下,用一句话概括你们的代码是怎么保证这 n 个正完全平方数互不相同的。讨论好之后(或者需要帮助),在 #discuss-queue
频道 @discuss 提问,带上你们的组号,然后说 "It fits!",课程人员会来听你们的解释,并给出反馈。
Scheme 列表和引用
Scheme 列表就是链表,快速复习一下:
nil
和()
是相同的:空列表。(cons first rest)
用于构造链表,其中first
是链表的第一个元素,rest
是剩余部分,通常也应该是一个列表。(car s)
返回列表s
的第一个元素。(cdr s)
返回列表s
的其余部分。(list ...)
接受 n 个参数,返回一个包含这些参数、长度为 n 的列表。(append ...)
接受 n 个列表作为参数,并返回一个包含这些列表所有元素的列表。(draw s)
绘制列表s
的链表结构。它只在 code.cs61a.org/scheme 上有效。现在尝试一下,例如(draw (cons 1 nil))
。
引用表达式使其不被求值。例子:
'four
和(quote four)
都求值为符号four
。'(2 3 4)
和(quote (2 3 4))
都求值为包含三个元素的列表:2、3 和 4。'(2 3 four)
和(quote (2 3 four))
求值为包含 2、3 和符号four
的列表。
下面是 list
和引用之间的一个重要区别:
scm> (list 2 (+ 3 4))
(2 7)
scm> `(2 (+ 3 4))
(2 (+ 3 4))
Q2:嵌套列表
用三种不同的方式创建下面描述的嵌套列表:使用 list
、quote
和 cons
。
首先,一起描述这个列表:“看起来有四个元素,第一个元素是……” 如果你遇到困难,请查看下面的提示。(但先尝试自己描述!)
一个包含四个元素的列表,其第一个元素是包含 a
和 b
的列表,第二个元素是 c
,第三个元素是 d
,第四个元素是仅包含 e
的列表。
接下来,使用对 list
的调用来构造此列表。 如果你运行这段代码,然后在 code.cs61a.org 中执行 (draw with-list)
,draw
过程会显示你所创建的列表结构。
(define with-list
(list
'YOUR-CODE-HERE
)
)
; (draw with-list) ; 取消注释此行以绘制 with-list
在 61A 代码中运行
每次调用 list 都会创建一个列表,并且此图中有三个不同的列表:一个包含 a
和 b
的列表:(list 'a 'b)
,一个包含 e
的列表:(list 'e)
,以及包含四个元素的整个列表:(list _ 'c 'd _)
。尝试将这些表达式放在一起。
现在,使用 quote
来构造此列表。
(define with-quote
'(
'YOUR-CODE-HERE
)
)
; (draw with-quote) ; 取消注释此行以绘制 with-quote
在 61A 代码中运行
一个引用表达式就足够了,但它需要符合 Scheme 语法的链表结构。因此,你的任务是理解该列表在 Scheme 中的表示方式。
上面绘制的嵌套列表是一个包含四个元素的列表,它的首尾元素均为列表:((a b) c d (e))
。引用这个表达式就能创建该列表。
现在,使用 cons
来构造此列表。不要使用 list
。你可以在答案中使用 first
。
(define first
(cons 'a (cons 'b nil)))
(define with-cons
(cons
'YOUR-CODE-HERE
)
)
; (draw with-cons) ; 取消注释此行以绘制 with-cons
在 61A 代码中运行
提供的 first
是结果的第一个元素,因此答案采用以下形式:
first ____
你可以用带引号的三个元素列表填充空白:
'(___ ___ ___)
c d (e)
或使用对 cons
的嵌套调用:
(cons ___ (cons ___ (cons ___ nil)))
c d (e)
Q3:配对
实现 pair-up
函数,该函数接收列表 s
作为输入,并返回一个列表的列表,其中包含 s
的所有元素,且顺序不变。结果列表中,每个子列表应包含 2 个元素,最后一个子列表最多可包含 3 个元素。
大家一起看看例子,确保都明白这个函数是做什么的。
;;; 返回由 s 元素组成的列表对。
;;;
;;; scm> (pair-up '(3 4 5 6 7 8))
;;; ((3 4) (5 6) (7 8))
;;; scm> (pair-up '(3 4 5 6 7 8 9))
;;; ((3 4) (5 6) (7 8 9))
(define (pair-up s)
(if (<= (length s) 3)
'YOUR-CODE-HERE
))
(expect (pair-up '(3 4 5 6 7 8)) ((3 4) (5 6) (7 8)) )
(expect (pair-up '(3 4 5 6 7 8 9)) ((3 4) (5 6) (7 8 9)) )
在 61A 代码平台运行
pair-up
接收一个数字列表,并返回一个列表的列表。因此,当 (length s)
小于等于 3 时,直接返回包含列表 s
的列表。例如,(pair-up (list 2 3 4))
应该返回 ((2 3 4))
。
使用 (cons _ (pair-up _))
构建结果,cons
的第一个参数是由 (car s)
和 (car (cdr s))
组成的双元素列表。而 pair-up
的参数则是去除前两个元素后的剩余部分。
讨论:对于哪个最长的列表 s
,(pair-up (pair-up s))
将返回一个只有一个元素的列表?(不要只是猜测和检查;讨论!)把你们的答案发到小组的文字聊天频道里。
登记参与
请大家填写 出勤表(每周每人提交一次)。
重要提示:离开前请帮忙把房间里的家具放回原位。谢谢!