1

我一直在玩The Little Schemer,并在我工作的过程中尝试想法(并非所有想法都在工作)。刚才,我在第 6 章(他们在其中介绍了辅助函数),我觉得这一步触手可及——我只是不知道如何迈出这一步。

书中的代码如下:

(define (^ b e) (expt b e))

(define (x a b) (* a b))

(define value-i
  (lambda (nexp)
    (cond
      ((atom? nexp) nexp)
      ((eq? (car (cdr nexp)) '+) (+ (car nexp) (value-i (caddr nexp))))
      ((eq? (car (cdr nexp)) 'x) (x (car nexp) (value-i (caddr nexp))))
      (else
        (^ (car nexp) (value-i (caddr nexp))))
      )))

定义了以下助手:

(define (operator-i aexp) (car (cdr aexp)))
(define (firstSubExp-i aexp) (car aexp))
(define (secondSubExp aexp) (caddr aexp))

并进行了这种简化:

(define value-i2
  (lambda (nexp)
    (cond
      ((atom? nexp) nexp)
      ((eq? (operator-i nexp) '+) (+ (firstSubExp-i nexp) 
                                     (value-i2 (secondSubExp nexp))))
      ((eq? (operator-i nexp) 'x) (x (firstSubExp-i nexp) 
                                     (value-i2 (secondSubExp nexp))))
      (else
        (eq? (operator-i nexp) '^) (^ (firstSubExp-i nexp) 
                                      (value-i2 (secondSubExp nexp))))
      )))

我认为我可以看到的简化是,当我们假设所有内容nexp都是有效的时,我们不需要重复((eq? (operator-i nexp) ...样板文件。

像下面这样的东西应该可以工作:

(define value-i3
  (lambda (nexp)
    (cond
      ((atom? nexp) nexp)
      (else
        ((operator-i nexp) 
         (firstSubExp-i nexp) 
         (value-i3 (secondSubExp nexp))))
      )))

...除了它没有。Exception: attempt to apply non-procedure ...它与(+、x 和 ^ 之一)出错。

我可以看到问题在于它无法识别我正在尝试使用(operator-i nexp).

我如何向解释器表明我希望应用我返回的函数(operator-i nexp)

也欢迎任何其他提示(我使用的是 petite chez 方案)。

4

2 回答 2

4

不幸的是,value-i3它不起作用,因为(operator-i nexp)返回的是一个符号,而不是一个过程对象。'+注意和之间的区别+

eval除了使用 alist 将符号与过程链接(或使用casecond如 Sylwester 的回答中提到的那样)之外,没有真正简单的方法来解决这个问题(如果我们排除,这是粗略且不推荐的):

(define symbol->procedure
  (let ((opmap `((+ ,+)
                 (x ,x)
                 (^ ,^))))
    (lambda (x)
      (cond ((assq x opmap) => cadr)
            (else #f)))))

然后使用symbol->procedure与 Sylwester 的答案相同的方式。

如果你觉得 quasiquotes 太难阅读,你可以直接使用listand cons

(define symbol->procedure
  (let ((opmap (list (cons '+ +)
                     (cons 'x x)
                     (cons '^ ^))))
    (lambda (x)
      (cond ((assq x opmap) => cdr)
            (else #f)))))

OP要求提供更多信息assq等,所以我想我会直接更新帖子。基本上,(assoc key alist)返回第一项是alistto ,否则返回;and类似于但使用and作为比较运算符。因此,这是一个示例实现(使用 R7RS/SRFI-1 语义):carequal?key#fassqassvassoceq?eqv?

(define (find pred lst)
  (cond ((null? lst) #f)
        ((pred (car lst)) (car lst))
        (else (find pred (cdr lst)))))

(define assoc
  (case-lambda
    ((key alist equal?)
     (find (lambda (x)
             (equal? key (car x)))
           alist))
    ((key alist)
     (assoc key alist equal?))))

(define (assq key alist)
  (assoc key alist eq?))

(define (assv key alist)
  (assoc key alist eqv?))
于 2013-12-26T21:47:40.773 回答
4

你假设的问题是operator-i返回一个符号。当您(+ 1 2)+过程进行评估时,这会使表单可调用。但是,在代码中的运算符位置,结果是符号'+。然后它会尝试应用符号而不是过程。这是两者的最小示例:

('+ 1 2) ; won't work
(+ 1 2)  ; works

您当然可以在它们之间进行查找:

(define (symbol->procedure sym)
  (case sym
    ((+) +)
    ((x) x)
    ((^) ^)
    (else #f)))

((symbol->procedure '+) 2 3) ; ==> 5
于 2013-12-26T22:03:44.723 回答