我想解决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
。
答案 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
ENTRY
是ZIP
,CITY
和STATE
的列表。但是,回想一下,列表是一系列以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
的第二个论点我将作为练习留给你。
我们差不多完成了!现在我们findzips
写find
是微不足道的。我们只需使用给定的STATE
和zipcodes
作为参数调用;; 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。
祝你好运!