scheme中的强制操作
前文为了实现同样类型的通用型计算法,采取了计算包的方式。
但还未考虑过,如果是不同类型的数据,该如何进行计算?例如有理数和实数、整数和复数?这之间如何调和。
scheme给出一个“强制”操作用来解决此类问题。
点击查看代码
(define (scheme-number->complex n)
(make-complex-from-real-imag (contents n) 0))
(put-coercion 'scheme-number 'complex scheme-number->complex)
(define (apply-generic op. args)
(let ((type-tags (map type-tag args)))
(let ((proc (get op type-tags)))
(if proc
(apply proc (map contents args))
(if (= (length args) 2))
(let ((type1 (car type-tags))
(type2 (cadr type-tags))
(a1 (car args))
(a2 (cadr args)))
(let ((t1->t2 (get-coercion type1 type2))
(t2->t1 (get-coercion type2 type1)))
(cond (t1->t2 (apply-generic op (t1->t2 a1) a2))
(t2->t1 (apply-generic op a1 (t2->t1 a2)))
(else (error "No method for these types" (list op type-tags))))))
(erroe "No method for these types" (list op type-tags))))))
点击查看代码
define (apply-generic op. args)
(let ((type-tage (map type-tag args)))
(let ((proc (get op type-tags)))
(if proc
(apply proc (map contents args))
(if (= (length args) 2))
(let ((type1 (car type-tags))
(type2 (cadr type-tags))
(a1 (car args))
(a2 (cadr args)))
(if (equal? type1 type2)
(error "No method for these types" (list op type-tags))
(let ((t1->t2 (get-coercion type1 type2))
(t2->t1 (get-coercion type2 type1)))
(cond (t1->t2 (apply-generic op (t1->t2 a1) a2))
(t2->t1 (apply-generic op a1 (t2->t1 a2)))
(else (error "No method for these types" (list op type-tags)))))))
(error "No method for these types" (list op type-tags))))))
点击查看代码
(define (attach-tag type-tag contents)
(cons type-tag contents))
(define (type-tag datum)
(if (pair? datum)
(car datum)
(error "Bad tagged datum" datum)))
(define (contents datum)
(if (pair? datum)
(cadr datum)
(error "Bad tagged datum" datum)))
(define (all-the-same? items)
(cond ((null? items) #t))
((= (length items) 1) #t)
(else (if (equal? (car items)(cadr items))
(all-the-same? (cdr items))
#f)))
(define (for-each-map ops items)
(if (null? ops)
'()
(cons ((car ops)(car items))
(for-each-map (cdr ops)(cdr items)))))
(define (map-all-or-false op items)
(if (null? items)
'()
(let ((first-ret (op (car items))))
(if first-ret
(let ((second-ret (map-all-or-false op (cdr items))))
(if second-ret
(cons first-ret second-ret)
#f))
#f))))
(define (get-coercion-list type-tags to-type)
(define (identity x) x)
(map-all-or-false (lambda (type)
(if (equal? type to-type)
identity
(get-coercion type to-type))) type-tags))
(define (try-coercion-list type-tags args)
(define (try-single-coercion type-tags args to-type)
(if (null? to-type)
#f
(let ((coercion-list (get-coercion-list type-tags to-type)))
(if coercion-list
(for-each-map coercion-list args)
(try-single-coercion type-tags args (cdr to-type))))))
(if (all-the-same? type-tags?)
#f
(try-single-coercion type-tags args (car type-tags))))
(define (apply-generic-list op args)
(let ((type-tags (map type-tag args)))
(let ((proc (get op type-tags)))
(if proc
(apply proc (map contents args))
(let ((coercion-args (try-coercion-args type-tags args)))
(if coercion-args
(apply-generic-list op coercion-args)
(error "No method for these types -- APPLY-GENERIC" (list op type-tags))))))))
(define (apply-generic op . args)
(apply-generic-list op args))
练习2.83 假定在设计一个通用型算数包,处理类型塔,请为每个类型设计一个过程,它能将该类型的对象提升到塔的上面一层。请说明如何安装一个通用的raise操作,使之能对各个类型工作。
点击查看代码
(define (raise x)
(let ((raise-proc (get 'raise (list (type-tag x)))))
(if raise-proc
(raise-proc (contents x))
#f)))
(define (install-rasie-package)
(put 'raise '(integer)
(lambda (x) (make-ractional x 1)))
(put 'raise '(ractional)
(lambda (x) (make-real (/ (number x) (denom x)))))
(put 'raise '(real)
(lambda (x) (make-complex-from-real-imag x 0))))
练习2.84 利用练习2.83里的raise操作修改apply-generic过程,使它能通过逐层提升的方法将参数强制到同样的类型。
点击查看代码
(define (raise-to-type x type)
(let ((x-type (type-tag x)))
(if (equal? x-type type)
x
(let ((x-raise (raise x)))
(if x-raise
(raise-to-type x-raise type)
#f)))))
(define (apply-generic op . args)
(let ((type-tags (map type-tag args)))
(let ((proc (get op type-tags)))
(if proc
(apply proc (map contents args))
(if (and (= (length args) 2) (not (equal? (car type-tags) (cadr type-tags))))
(let ((a1 (car args))
(a2 (cadr args)))
(let ((a1-raise (raise-to-type a1 (type-tag a2))))
(if a1-raise
(apply-generic op a1-raise a2)
(let ((a2-raise (raise-to-type a2 (type-tag a1))))
(if a2-raise
(apply-generic op a1 a2-raise))
(error "No method for these types" (list op type-tags))))))
(error "No method for these types" (list op type-tags)))))))

浙公网安备 33010602011771号