5

问题如下,它在http://www.cs.indiana.edu/classes/b551-leak/scheme_practice.html中找到。

问题定义:编写一个函数 cxr,它是 Scheme 中提供的 car/cdr 运算符的泛化。cxr 应该接受一个字符串 "a"s 和 "d"s 代表要执行的 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 之间进行链接...我尝试了字符串-> 符号,但是输出被引用并且函数没有被执行。- 那么有什么方法可以取消引用?!

真正的问题:如何解决这个问题?


更新:感谢所有这些答案。它们都是正确的,我什至在发布问题之前就已经通过这种方式解决了。我主要是在寻找一种在输入为 (cxr adddd) 时实际调用 caddddr 的方法...... Everbody 的功能与 caddddr 相同,但实际上并未调用 cadddr。

也就是说,如何使函数具有与 cadr cadr 等相同的命名类型。


更新:(我想我找到了解决方案,它如下 - 但正如下面所说,它不适用于更长的 d):

(define cxr 
(lambda (ad l)
   ( (eval (string->symbol (string-append "c" ad "r")))   l)
)
)
4

4 回答 4

5

正如 Mimisbrunnr 指出的那样,这里的想法不是字符串附加然后 eval。一方面,这不适用于较长的 a 和 d 序列。

相反,您想编写一个函数,该函数使用一个字符串并通过逐个字符分析字符串来返回一个函数。

在 HtDP 用语中,这可以在将字符串转换为列表后,在“a”和“d”列表上作为结构递归来完成。

为了使这更容易,您可以使用“string->list”。这存在于 Racket 中,我有一种模糊的感觉,它也是 r5rs 的一部分。

于 2012-03-08T04:29:05.877 回答
3

您问:“现在我如何将“cadr”与 cadr 联系起来。

首先我们可以将字符#\a 链接到car,将#\d 链接到cdr:

(define (x->cxr x)
  (if (eqv? x #\a) car cdr))

例子:

> ((x->cxr #\a) '(foo bar)) 
'foo

然后使用是andcadr的组成的事实(如 cadr is .carcdr(compose car cdr)

(define (cxr s)
  (apply compose
         (map x->cxr 
              (string->list s))))
于 2012-03-08T14:34:32.673 回答
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))))

我将问题分为三个部分:

  1. cxr返回一个函数,该函数在调用时将根据作为参数接收的操作序列处理其输入。请注意,操作列表的处理顺序与声明它们的顺序相反
  2. generate-cxr依次处理每个操作,直到没有更多操作可以执行,累积结果
  3. operate决定应用哪个操作并实际执行它,返回结果

上述过程返回正确答案但效率很低,因为操作的句法分析与它们的执行交错​​。可以将句法分析与执行分开,从而产生更快的解决方案。请参阅SICP中的第4.1.7节。

于 2012-03-08T15:02:42.963 回答
1

编辑:为了响应您的更新,我认为您想做这样的事情:

(eval `(define ,<procedure-to-return-symbol> ,<value>) <environment>)

例如在 mit-scheme 中:

(eval `(define ,(string->symbol "abc") ,(* 2 2)) user-initial-environment)

user-initial-environment当输入到 REPL 中时,符号被实习的环境在哪里。此示例将返回abc与值 4 关联的符号。使用此方法,您将能够使用您的过程来创建名称并将其与下面我的解决方案返回的值相关联。您可以在此处阅读更多关于eval和 mit-scheme 环境的信息。 </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沿着 a's 和 d's 调用的列表向下运行,car或者cdr在下一次调用的结果上运行helper- 它为lambda. 当列表为空时,helper返回arg哪个是lambda表达式的参数。由于lambda表单在定义中没有传递给它的参数,因此cxr将返回一个过程。

于 2012-03-11T21:33:23.670 回答