我对scheme和car、cdr等概念非常陌生。我有这个函数来返回列表的最后一个元素,但现在它只返回一个空列表。
(define (last mylist)
(if (list? mylist)
(if (null? mylist)
(if (null? (cdr mylist))
'()
(last (cdr mylist))
)
)
)
)
《如何设计程序》一书通过为您提供具体而详细的设计方法来帮助您解决这个问题。第9.2节"非空列表"涵盖了这一特殊问题。大体上,以下是您需要遵循的步骤:
- 为非空列表制定数据定义(或取自书本)
- 为您的职能编写目的说明、签名和标题
- 编写测试用例(您的测试用例将在此处帮助批量。(请记住,您不需要测试数据定义不允许的输入)
- 添加与数据定义关联的模板(也出现在本书中)
- 填写模板中的空白处以完成定义
- 调试
仅凭缩进,很明显您是从另一种编程语言进入Scheme的
但是您也错误地使用了if
——在Scheme中,不能有单个分支if
语句。Scheme中根本没有语句,只有表达式和if
表达式将始终采用3个操作数(自变量)
- 谓词(条件)
- 结果(如果谓词为true会发生什么)
- 替代(谓词为false时会发生什么)
您的程序已关闭。只要稍微调整一下,就可以到达需要的位置——注意缩进是如何使查看if
的3个操作数变得容易的。
(define (last mylist)
(if (null? mylist)
#f
(if (null? (cdr mylist))
(car mylist)
(last (cdr mylist)))))
最后,Scheme提供了cond
,有助于防止条件序列中不必要的代码嵌套
(define (last mylist)
(cond ((null? mylist)
#f)
((null? (cdr mylist))
(car mylist))
(else
(last (cdr mylist)))))
(last '())
;; #f
(last '(1))
;; 1
(last '(1 2))
;; 2
(last '(1 2 3))
;; 3
除了这个答案的范围之外,还有(last '())
的返回值#f
——我认为在空列表上调用last
应该与在空列表中调用car
具有相同的效果。但我还是交给你吧。
如果(null? mylist)
不成立,它是什么?非空列表。
非空列表可以有一个或多个元素。
有多少元素有这样的列表,它们的最后一个元素是它们的第一个?
关于这些名单的cdr
,我们能说些什么呢?您应该使用它来提前停止递归。现在它一直持续到列表为空,但您需要提前停止。
(define (last mylist)
(if (list? mylist)
(if (null? mylist)
'()
;; {1}
(last (cdr mylist))
;;
)))
在{1}
,您无条件地呼叫(last (cdr mylist))
。但是,如果你已经到达了你的清单的末尾呢?如果只剩下一个元素怎么办?在这种情况下,您需要返回it作为结果。因此,将无条件代码替换为if
表达式即可完成此操作。
当您询问问题(null? (cdr mylist))
时,如果这是真的,您应该返回(car mylist)
而不是'()
。因为在这一点上它意味着CCD_ 18是一个单原子列表。
(define (last mylist)
(cond ((null? mylist) '())
((null? (cdr mylist)) (car mylist))
(else (last (cdr mylist)))))
您可以使用cond
而不是if
来避免嵌套条件,因为cond
处理许多臂,而if
通常用于一个条件只有两个选项的情况。
这本书小Schemer在帮助我想象Scheme项目中发生的事情方面做得最好。
我认为这最接近您的初始代码:
(define (last mylist)
(if (list? mylist)
(if (null? mylist)
'() ; input list is empty
(if (null? (cdr mylist))
(car mylist) ; list only has one remaining element so this is it
(last (cdr mylist)))) ; otherwise, recurse
#f)) ; input is not a list
使用if
时,请确保始终填写true和false分支。