问题描述
|
我想解决方案(R5RS)中的问题。这是我的示例数据:
(define zipcodes \'(
(96774 ookala hawaii)
(90001 losangeles california)
(90263 malibu california)
(10044 newyork newyork)
))
每个列表元素的格式为“ 1”。我想创建一个像这样工作的函数:将STATE作为输入传递,并且该函数返回该状态的邮政编码(如果没有用于此状态的元素,则返回一个空列表)。
>(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
。
解决方法
首先,您发布的代码中有一个错字:“ 6”应为“ 7”。
让我们看一下带有适当缩进的函数:
(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))
。一个好方法是叫ѭ5,但您的作业要排除在外。您可以重复通话。而不是通过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
尚未完成:您会注意到此函数始终会引发异常。您会仔细研究列表,最终到达末尾……并cho之以鼻,因为您不处理列表为空的情况。我将把它保留为练习。
现在,还有其他方法可以编写此函数(实际上,更好的方法,但是对于初学者来说更难理解)。他们可能不是您计划的目的。假设您只想对find
进行一次递归调用,而没有let
或任何其他方式来存储调用find
的结果。然后,您别无选择,只能调用find
,但是根据是否找到该元素使用不同的参数。你该怎么做? (提示:您将需要第三个参数。)
,理解此问题的第一步是明确声明我们正在使用的数据由什么组成。让我们写下一系列数据定义:
;; A ZIP is a number
;; A CITY is a symbol
;; A STATE is a symbol
ENTRY
是ZIP
,a21ѭ和STATE
的列表。但是,请记住,列表是一系列以null结尾的con单元格。让我们使用对cons
的显式调用为ENTRY
定义数据定义:
;; 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
。任何时候我们必须处理一个以上的情况时,都可以对每种情况使用use34ѭ表示我们想要的行为:
;; 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 ...)))
到目前为止,一切都很好。现在是困难的部分。如果输入列表不为空,则它必须至少包含一个“ 19”。对于任何给定的ENTRY
,我们都必须处理两种情况:条目中的状态与我们要寻找的ѭ22matches相匹配,否则就没有。假设我们有一个函数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
让我们先处理第二种情况。如果我们要搜索的ѭ22与条目中下一个ѭ19中的ѭ22不匹配,我们只希望列表中其余部分中的任何ѭ20都匹配。查看'find \'的合同,我们可以看到在列表的其余部分上调用find可以准确地满足我们的需求!填补这一点,我们有:
;; 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
在第一种情况下,我们知道\'entires \'中的第一个ENTRY
具有与我们要搜索的STATE
匹配的状态。考虑在这种情况下我们想要的最终结果:我们想要一个列表,该列表以我们已经知道的ZIP
开头,与我们要搜索的STATE
匹配,其后是其他任何ZIP
且其状态在其余部分也都匹配entries
。我们可以使用ѭ12来构建一个新列表。 cons
具有以下合同:
;; cons : any [listof any] -> [listof any]
cons
的第一个参数很简单。再次假设我们有一个函数'get-zip \',对于给定的ENTRY
,我们可以得到ZIP
。然后我们可以简单地从ENTRY
中提取ZIP
:
;; 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))))))
ѭ12的第二个参数我将作为练习留给您。
我们快完成了!现在我们有find
写作findzips
是微不足道的。我们简单地用给定的STATE
和zipcodes
称为find
。填写其余的椭圆,您将完成:
;; 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))
在解决此问题时,我们使用了“设计食谱”的元素,这是设计程序的逐步指南。 Felleisen,Findler,Flatt和Krishnamurthi在“如何设计程序”中完整概述了“设计食谱”。全文可在线获得:www.htdp.org。
祝好运!