返回计划中的列表

时间:2011-05-24 21:45:04

标签: list scheme

我想解决Scheme(R5RS)中的问题。这是我的示例数据:

(define zipcodes '(
 (96774 ookala hawaii)
 (90001 losangeles california)
 (90263 malibu california)
 (10044 newyork newyork)
 ))

每个列表元素的格式为(ZIP CITY STATE)。我想创建一个像这样工作的函数:传递STATE作为输入,函数返回此状态的zipcodes(如果没有此状态的元素,则返回一个空列表)。

>(findzips 'california)
(90001 900263)

>(findzips 'berlin)
empty

我通过下面的代码尝试这个,但它只返回第一个值而不是列表

(define (findzips x)
  (find x zipcodes))
(define find
  (lambda (x zipcodes)
    (if (EQ? x (caddr (car zipcodes)))
        (list (car (car zipcodes)))
      (find x (cdr zipcodes)))))

我不允许使用!个函数或let

2 个答案:

答案 0 :(得分:3)

首先,您发布的代码中存在拼写错误:find3应为find

让我们看看具有适当缩进的函数:

(define find
  (lambda (x zipcodes)
    (if (EQ? x (caddr (car zipcodes)))    ; if the current entry matches
        (list (car (car zipcodes)))       ; then return it, in a single-element list
      (find x (cdr zipcodes)))))          ; else return the subsequent matches

如果当前条目匹配,则不会尝试查找任何后续匹配。因此,您返回的列表将永远不会包含多个元素。

您始终需要查找后续元素,即您始终需要调用(find x (cdr zipcodes))。一个好方法是打电话给let,但是你的家庭作业会指出这一点。你可以复制一下这个电话。不是使用list过程创建单个元素列表,而是构建一个列表,其中包含当前匹配,后跟后续匹配列表。将一个元素添加到列表的过程是cons

(define find
  (lambda (x zipcodes)
    (if (EQ? x (caddr (car zipcodes)))    ; if the current entry matches
        (cons (car (car zipcodes))        ; then return it, followed by…
              (find x (cdr zipcodes)))    ; … the subsequent matches
      (find x (cdr zipcodes)))))          ; else return the subsequent matches

这还没有完成:你会注意到这个函数总是抛出一个异常。你仔细研究列表,最终到达终点......然后因为当列表为空时你没有处理这个案子而窒息。我会留下这个练习。

现在还有其他方法来编写这个函数(事实上,更好的方法,但对初学者来说更难理解)。它们可能是也可能不是您作业中的目的。想象一下,您只想对find进行一次递归调用,而不是let或任何其他方式来存储调用find的结果。然后你别无选择,只能调用find,但使用不同的参数,具体取决于元素是否被找到。你怎么能这样做? (提示:你需要第三个论点。)

答案 1 :(得分:3)

理解这个问题的第一步是明确说明我们正在处理的数据是什么。让我们写下一系列数据定义:

;; A ZIP is a number
;; A CITY is a symbol
;; A STATE is a symbol

ENTRYZIPCITYSTATE的列表。但是,回想一下,列表是一系列以null结尾的cons单元格。让我们使用对ENTRY的显式调用来编写cons的数据定义:

;; An ENTRY is of the form (cons ZIP (cons CITY (cons STATE null)))

;; A [listof ENTRY] is either:
;;
;;   1. The empty list, null, or
;;   2. (cons ENTRY [listof ENTRY])

掌握了这些数据定义后,更容易准确了解我们希望我们的功能做什么。我们将为函数写下一个包含两部分的契约:函数的名称和函数消耗的数据类型以及函数生成的数据类型。例如:

;; <FUNCTION NAME> : <WHAT OUR FUNCTION CONSUMES> -> <WHAT OUR FUNCTION PRODUCES>

现在我们可以为我们想写的函数写一份合约:

;; find : STATE [listof ENTRY] -> [listof ZIP]
;; Produces the ZIPs in [listof ENTRY] that match the given STATE.
(define (find state entries)
  ...)

;; findzips : STATE -> [listof ZIP]
;; Produces the ZIPs in 'zipcodes' that match the given STATE.
(define (findzips state)
  ...)

让我们开始填写find的定义。我们从合同中知道'find'有两个参数,STATE[listof ENTRY]。我们从[listof ENTRY]的数据定义中了解到,它可以是以下两种方式之一:(cons ENTRY [listof ENTRY])null。任何时候我们必须处理多个案例,我们可以在每种情况下使用cond来表达我们想要的行为:

;; find : STATE [listof ENTRY] -> [listof ZIP]
;; Produces the ZIPs for which there is an ENTRY in the [listof ENTRY]
;; with a STATE that matches the one given.
(define (find state entries)
  (cond ((null? entries) ...)
        (else ...)))

如果您不熟悉cond,请参阅您的教科书或询问教师。

到目前为止一切顺利。那么如果它被称为null(我们的合同给出了有效的输入),我们的函数应该返回什么?我们的合同建议我们应该返回一个空列表。

;; find : STATE [listof ENTRY] -> [listof ZIP]
(define (find state entries)
  (cond ((null? entries) null)
        (else ...)))

到目前为止一切顺利。现在是困难的部分。如果输入列表不为空,则它必须至少包含一个ENTRY。对于任何给定的ENTRY,我们必须处理两种情况:条目中的状态与我们正在寻找的STATE匹配,或者它不匹配。让我们假设我们有一个函数get-state,给定ENTRY给我们STATE

;; find : STATE [listof ENTRY] -> [listof ZIP]
(define (find state entries)
  (cond ((null? entries) null)
        (else (if (equal? state
                          (get-state (car entries)))
                  ... ;; matches
                  ...)))) ;; doesn't match

让我们先处理第二个案例。如果我们搜索的STATE与条目中下一个STATE中的ENTRY不匹配,我们只需要其他ZIP匹配的其他;; find : STATE [listof ENTRY] -> [listof ZIP] (define (find state entries) (cond ((null? entries) null) (else (if (equal? state (get-state (car entries))) ... ;; matches ...)))) ;; doesn't match 的清单。查看“查找”合同,我们可以看到在列表的其余部分调用查找将为我们提供我们想要的内容!填写我们有:

ENTRY

在第一种情况下,我们知道'entires'中的第一个STATE的状态与我们正在搜索的ZIP匹配。在这种情况下,请考虑我们对最终结果的要求:我们希望以我们已经知道的STATE开头的列表与我们正在搜索的ZIP匹配,然后是其他任何entries } s的状态也与cons的其余部分匹配。我们可以使用cons来构建新列表。 ;; cons : any [listof any] -> [listof any] 有以下合同:

cons

ZIP的第一个参数很简单。让我们再次假设我们有一个函数'get-zip',它为给定的ENTRY提供了ZIP。然后,我们可以从ENTRY

中简单地提取;; find : STATE [listof ENTRY] -> [listof ZIP] (define (find state entries) (cond ((null? entries) null) (else (if (equal? state (get-state (car entries))) (cons (get-zip (car entries)) ...) (find state (cdr entries))))))
cons

find的第二个论点我将作为练习留给你。

我们差不多完成了!现在我们findzipsfind是微不足道的。我们只需使用给定的STATEzipcodes作为参数调用;; get-zip : ENTRY -> ZIP ;; Produces the ZIP for a given ENTRY. (define (get-zip entry) ...) ;; get-state : ENTRY -> STATE ;; Produces the STATE for a given ENTRY. (define (get-state entry) ...) ;; find : STATE [listof ENTRY] -> [listof ZIP] ;; Produces the ZIPs in [listof ENTRY] that match the given STATE. (define (find state entries) (cond ((null? entries) null) (else (if (equal? state (get-state (car entries))) (cons (get-zip (car entries)) ...) (find state (cdr entries)))))) ;; findzips : STATE -> [listof ZIP] ;; Produces the ZIPs in 'zipcodes' that match the given STATE. (define (findzips state) (find state zipcodes)) 。填写剩余的省略号,你将完成:

{{1}}

在解决这个问题时,我们使用了“设计食谱”的元素,这是设计程序的分步指南。 Felleisen,Findler,Flatt和Krishnamurthi在“如何设计程序”中详细介绍了“设计方案”。全文可在线获取:www.htdp.org。

祝你好运!