Exercise 4.14
1、Eva的map能够工作是应该的,因为Eva在求值器中定义了map。
2、那么Louis的map为什么不能工作呢?
首先,让我们来看一下map的基本使用方式(在scheme中,而不是在求值器中):
(map (lambda (x) (* 2 x)) '(1 2 3 4)) => (2 4 6 8)可以看出map函数的使用方式和其他作为基本过程安装的函数(如cons,cdr等)的区别是它接受一个函数作为参数。好的,假设我们按照louis的方式实现map,现在我们来看看当我们的求值器接受上面的求值器时,会发生什么:
1)根据求值器规则,我们首先求值操作符map和参数的值:
;;; M-Eval input: map ;;; M-Eval value: (primitive #<procedure:mmap>) ;;; M-Eval input: (lambda (x) (* 2 x)) ;;; M-Eval value: (compound-procedure (x) ((* 2 x)) <procedure-env>) ;;; M-Eval input: '(1 2 3 4) ;;; M-Eval value: (1 2 3 4)
> map #<procedure:mmap> > (lambda (x) (* 2 x)) #<procedure> > '(1 2 3 4) (1 2 3 4)
2)在求值过程调用时,求值器看到(primitive #<procedure:mmap>),它会取出系统的map版本,即#<procedure:mmap>,然后把它应用在参数(compound-procedure ...)上,这时map就会报错:
procedure application: expected procedure, given: (procedure (x) ((* 2 x)) ……看到了吧,系统函数map期待的是#<procedure>类型的过程,而我们给的是(procedure (x) ((* 2 x)) 这样的列表。这个列表是由我们的求值器中的函数产生的:
(define (make-procedure parameters body env) (list 'procedure parameters body env))因为系统版本的map不知道如何处理我们的求值器产生的过程,因此系统版本的map不能正常工作。
3,如何正确实现map?
或许最好的理解方式是像Eva一样自己实现一个map:
;; Exercise 4.14 (define (eval-map exp env) (let ((proc (cadr exp)) (alist (cddr exp))) (apply-in-underlying-scheme map (lambda (x) (apply (eval proc env) (list x))) (list-of-values alist env)))) (put 'map eval-map)测试结果:
;;; M-Eval input: (map (lambda (x) (* x 2)) '(1 2 3)) ;;; M-Eval value: (2 4 6)注意:这里实现的map只是最基本的map函数,它只能接受一个列表,因为我们的求值器处理任意多个的参数的情况,像下面这样的方式map不能工作:
(map + '(1 2 3) '(4 5 6))