如何在方案中将字符串作为函数名传递? [Scheme中函数名称的动态构造]

时间:2012-03-08 04:16:41

标签: scheme mit-scheme

问题如下,它在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)
)
)

4 个答案:

答案 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

然后使用cadrcarcdr的组成这一事实(在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))))

我将问题分为三个部分:

  1. cxr返回一个函数,该函数在被调用时将根据作为参数接收的操作序列处理其输入。请注意,操作列表按照声明它们的相反顺序进行处理
  2. generate-cxr依次处理每个操作,直到不再执行任何操作,累积结果
  3. operate决定应用哪个操作并实际执行,返回结果
  4. 上述过程会返回正确的答案,但效率非常低,因为操作的句法分析与其执行交错。可以将语法分析与执行分开,从而产生更快的解决方案。请参阅4.1.7中的SICP部分。

答案 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>

在已定义carcdr的成分的情况下,您的初始解决方案最多可用。问题是寻找一个过程,该过程返回一个过程,该过程将列表的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的结果中向下调用cdrhelper列表中的lambda - 它构建了helper。当列表为空时,arg将返回lambda,这是lambda表达式的参数。由于cxr表单在定义中没有传递给它的参数,{{1}}将返回一个过程。