问题如下,它在http://www.cs.indiana.edu/classes/b551-leak/scheme_practice.html中找到。
问题定义: 编写一个函数cxr,它是Scheme中提供的car / cdr运算符的泛化。 cxr应该带一串“a”和“d”表示要执行的car和cdr操作的序列,并返回一个能够执行该序列的函数。
因此(cxr“ad”)相当于函数cadr。
((cxr "ad") '(i ii iii iv v vi vii)) ==> ii
(define sixth (cxr "addddd"))
(sixth '(i ii iii iv v vi vii)) ==> vi
我的尝试: 我使用string-append将cxr“ad”转换为字符串“cadr”。 [这很容易] ..现在我如何在“cadr”与cadr之间建立链接...我尝试了string->符号,但输出被引用,并且该函数从未被执行。 - 那么有什么方法可以取消引用吗?
真正的问题:如何解决这个问题?
更新: 谢谢大家的回答。它们都是正确的,我甚至在发布问题之前就已经解决了这个问题。当输入是(cxr adddd)时,我主要是想找到一种实际调用caddddr的方法... Everbody和caddddr的功能相同,但实际上并没有调用cadddr。
也就是说,如何使函数具有与cadr caddr等相同的命名类型。
更新:(我认为我找到了解决方案,它如下 - 但正如下面所说,它不适用于更长的时间):
(define cxr
(lambda (ad l)
( (eval (string->symbol (string-append "c" ad "r"))) l)
)
)
答案 0 :(得分:5)
正如Mimisbrunnr所指出的,这里的想法不是字符串附加,而是eval。首先,这对于a和d的较长序列不起作用。
相反,您希望编写一个使用字符串的函数,并通过逐字符分析字符串来返回函数。
在HtDP的说法中,在将字符串转换为列表之后,可以在“a”和“d”列表上进行结构递归。
为了简化这一过程,您可以使用“string-> list”。这存在于Racket中,我有一种模糊的感觉,它也是r5rs的一部分。
答案 1 :(得分:3)
你问:“现在我如何将”cadr“与cadr联系起来。
首先,我们可以将字符#\ a链接到car,将#\ d链接到cdr:
(define (x->cxr x)
(if (eqv? x #\a) car cdr))
示例:
> ((x->cxr #\a) '(foo bar))
'foo
然后使用cadr
是car
和cdr
的组成这一事实(在cadr中为(compose car cdr)
。
(define (cxr s)
(apply compose
(map x->cxr
(string->list s))))
答案 2 :(得分:1)
这是一个可能的实现,一个迷你解释器,用于在给定输入上应用的操作列表:
(define (cxr ops)
(lambda (input)
(generate-cxr (reverse (string->list ops)) input)))
(define (generate-cxr ops-list acc)
(if (null? ops-list)
acc
(generate-cxr (cdr ops-list) (operate (car ops-list) acc))))
(define (operate op input)
(cond ((eqv? op #\a) (car input))
((eqv? op #\d) (cdr input))
(else (error "unknown operation" op))))
我将问题分为三个部分:
答案 3 :(得分:1)
编辑:为了回应您的更新,我认为您想要做这样的事情:
(eval `(define ,<procedure-to-return-symbol> ,<value>) <environment>)
e.g。在mit-scheme中:
(eval `(define ,(string->symbol "abc") ,(* 2 2)) user-initial-environment)
其中user-initial-environment
是在键入REPL时符号被插入的环境。此示例将返回与值4关联的符号abc
。使用此方法,您可以使用过程创建名称并将其与下面的解决方案返回的值相关联。您可以阅读有关eval
和mit-scheme环境here的更多信息。
</edit>
EDIT2:一个明确的解决方案:
(define (cxr x)
(define (helper xlist arg)
(cond ((null? xlist) arg)
((eq? (car xlist) #\a) (car (helper (cdr xlist) arg)))
((eq? (car xlist) #\d) (cdr (helper (cdr xlist) arg)))
(else (error "INVALID ARGS FOR CXR"))))
(eval `(define ,(string->symbol (string-append "c" x "r"))
,(lambda (arg) (helper (string->list x) arg))) user-initial-environment))
通过这种方式,您可以为任何深度的“广告”字符串创建命名过程。</edit>
在已定义car
和cdr
的成分的情况下,您的初始解决方案最多可用。问题是寻找一个过程,该过程返回一个过程,该过程将列表的car / cdr带到任意深度。这是我的解决方案:
(define (cxr x)
(define (helper xlist arg)
(cond ((null? xlist) arg)
((eq? (car xlist) #\a) (car (helper (cdr xlist) arg)))
((eq? (car xlist) #\d) (cdr (helper (cdr xlist) arg)))
(else (error "INVALID ARGS FOR CXR"))))
(lambda (arg) (helper (string->list x) arg)))
helper
在下一次调用car
的结果中向下调用cdr
或helper
列表中的lambda
- 它构建了helper
。当列表为空时,arg
将返回lambda
,这是lambda
表达式的参数。由于cxr
表单在定义中没有传递给它的参数,{{1}}将返回一个过程。