(apply-generic op . args)

教育者, 将軍, 栄養士, 心理学者, 親はプログラムする. 軍隊, 学生, 一部の社会はプログラムされる. - 計算機プログラムの構造と解釈 序文

SICP 2.5 汎用演算のシステム

問題2.85で意図しない動作に。 修正しようにも1fileで雑に書き進めたので修正厳しいので2.85以降は飛ばすことに。

もうapply-genericええやろ...(疲労)

2週目、又は一旦全部終わったら2.4と2.5をやり直そうと思います。 1周目から完璧を求めると挫折するので妥協を...いやこれは妥協ではない、政治的妥結である(錯乱

そういえばググってget/putとかコピペして使えるようにしながら進めたけど、これ本来は「動かないコード」なんですよね... Try&Errorしないで脳内だけで進めるの、厳しすぎじゃないですか?(やれる人たち天才かよ...)

(load "./2.4.scm")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; https://sicp.sourceacademy.org/chapters/2.5.1.html
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (add x y) (apply-generic 'add x y))
(define (sub x y) (apply-generic 'sub x y))
(define (mul x y) (apply-generic 'mul x y))
(define (div x y) (apply-generic 'div x y))

(define (install-scheme-number-package)
  (define (tag x)
    (attach-tag 'scheme-number x))

  (put 'add '(scheme-number scheme-number)
       (lambda (x y) (tag (+ x y))))
  (put 'sub '(scheme-number scheme-number)
       (lambda (x y) (tag (- x y))))
  (put 'mul '(scheme-number scheme-number)
       (lambda (x y) (tag (* x y))))
  (put 'div '(scheme-number scheme-number)
       (lambda (x y) (tag (/ x y))))

  ; ex-2.79追加
  (put 'equ? '(scheme-number scheme-number) =)
  ; ex-2.80追加
  (put 'zero? '(scheme-number)
       (lambda (x) (= x 0)))
  ; ex-2.81追加
  (put 'exp '(scheme-number scheme-number)
       (lambda (x y) (tag (expt x y))))

  ; ex-2.83追加
  (put 'raise '(scheme-number)
       (lambda (x) (make-rational x 1)))

  (put 'make 'scheme-number
       (lambda (x) (tag x)))

  'done)

(define (make-scheme-number n)
  ((get 'make 'scheme-number) n))

(define (install-rational-package)
  ;; internal procedures
  (define (numer x) (car x))
  (define (denom x) (cdr x))

  (define (make-rat n d)
    (print "make-rat " n d)
    (let ((g (gcd n d)))
      (cons (/ n g) (/ d g))))

  (define (add-rat x y)
    (make-rat (+ (* (numer x) (denom y))
                 (* (numer y) (denom x)))
              (* (denom x) (denom y))))
  (define (sub-rat x y)
    (make-rat (- (* (numer x) (denom y))
                 (* (numer y) (denom x)))
              (* (denom x) (denom y))))
  (define (mul-rat x y)
    (make-rat (* (numer x) (numer y))
              (* (denom x) (denom y))))
  (define (div-rat x y)
    (make-rat (* (numer x) (denom y))
              (* (denom x) (numer y))))

  ; ex-2.79追加
  (define (equ? x y)
    (= (* (numer x) (denom y)) (* (numer y) (denom x))))
  ; ex-2.80追加
  (define (zero? x)
    (= 0 (numer x)))

  ; ex-2.83追加
  (define (raise-rat x)
    (make-real (/ (* (numer x) 1.0) (denom x))))

  ;; interface to rest of the system
  (define (tag x) (attach-tag 'rational x))
  (put 'add '(rational rational)
       (lambda (x y) (tag (add-rat x y))))
  (put 'sub '(rational rational)
       (lambda (x y) (tag (sub-rat x y))))
  (put 'mul '(rational rational)
       (lambda (x y) (tag (mul-rat x y))))
  (put 'div '(rational rational)
       (lambda (x y) (tag (div-rat x y))))

  ; ex-2.79追加
  (put 'equ? '(rational rational) equ?)
  ; ex-2.80追加
  (put 'zero? '(rational) zero?)
  ; ex-2.83追加
   (put 'raise '(rational)
       (lambda (x) (make-real (/ (* (numer x) 1.0) (denom x)))))
  ; (put 'raise '(rational)
  ;     (lambda (x) (raise-rat x)))

  ; ex-2.85追加
  (put 'project 'rational
       (lambda (x) (make-scheme-number (round (/ (numer x) (denom x))))))

  (put 'make 'rational
       (lambda (n d) (tag (make-rat n d))))
  'done)

(define (make-rational n d)
  ((get 'make 'rational) n d))


; ex-2.77
; (define z3 (make-complex-from-real-imag 1 2))
; (magnitude z3)
; apply-genericは2回呼び出される.
;
; * 最初にdefine z3が実行される
; * z3は(complex rectangular 1 . 2)になる
;
; * 2.4.scmのdefine magnitudeが実行される
;   * zは(complex rectangular 1 . 2)
; * apply-genericが呼び出される
; * proc実行されてcomplexタグのmagnitudeが呼び出される
; * complexのmagnitudeから2.4.scmで定義されたdefine magnitudeがもう一度呼び出される
;   * zは(rectangular 1 . 2)
; * apply-genericが呼び出される
; * proc実行されてrectangularタグのmagnitudeが呼び出される
; * rectangularのmagnitudeが実行されて計算される

(define (install-complex-package)
  ;; imported procedures from rectangular and polar packages
  (define (make-from-real-imag x y)
    ; (print "complex make-from-real-imag")
    ((get 'make-from-real-imag 'rectangular) x y))
  (define (make-from-mag-ang r a)
    ((get 'make-from-mag-ang 'polar) r a))

  ;; internal procedures
  (define (add-complex z1 z2)
    (make-from-real-imag (+ (real-part z1) (real-part z2))
                         (+ (imag-part z1) (imag-part z2))))
  (define (sub-complex z1 z2)
    (make-from-real-imag (- (real-part z1) (real-part z2))
                         (- (imag-part z1) (imag-part z2))))
  (define (mul-complex z1 z2)
    (make-from-mag-ang (* (magnitude z1) (magnitude z2))
                       (+ (angle z1) (angle z2))))
  (define (div-complex z1 z2)
    (make-from-mag-ang (/ (magnitude z1) (magnitude z2))
                       (- (angle z1) (angle z2))))

  ; ex-2.79追加
   (define (equ? x y)
     (and (= (real-part x) (real-part y)) (= (imag-part x) (imag-part y))))
  ; ex-2.80追加
   (define (zero? x)
     (and (= (real-part x) 0) (= (imag-part x) 0)))

  ;; interface to rest of the system
  (define (tag z) (attach-tag 'complex z))
  (put 'add '(complex complex)
       (lambda (z1 z2) (tag (add-complex z1 z2))))
  (put 'sub '(complex complex)
       (lambda (z1 z2) (tag (sub-complex z1 z2))))
  (put 'mul '(complex complex)
       (lambda (z1 z2) (tag (mul-complex z1 z2))))
  (put 'div '(complex complex)
       (lambda (z1 z2) (tag (div-complex z1 z2))))

  ; ex-2.77追加
  (put 'real-part '(complex) real-part)
  (put 'imag-part '(complex) imag-part)
  (put 'magnitude '(complex) magnitude)
  (put 'angle '(complex) angle)

  ; ex-2.79追加
  (put 'equ? '(complex complex) equ?)

  ; ex-2.80追加
  (put 'zero? '(complex) zero?)

  ; ex-2.85追加
  (put 'project 'complex
       (lambda (x) (make-real (real-part x))))

  (put 'make-from-real-imag 'complex
       (lambda (x y) (tag (make-from-real-imag x y))))
  (put 'make-from-mag-ang 'complex
       (lambda (r a) (tag (make-from-mag-ang r a))))
  'done)

(define (make-complex-from-real-imag x y)
  ((get 'make-from-real-imag 'complex) x y))

(define (make-complex-from-mag-ang r a)
  ((get 'make-from-mag-ang 'complex) r a))

(install-scheme-number-package)
(install-rational-package)
(install-complex-package)

; (define z3 (make-complex-from-real-imag 1 2))
; (print z3)
; (print "****************************")
; (print (magnitude z3))

; ex-2.78
(define (attach-tag type-tag contents)
  (if (eq? type-tag 'scheme-number)
    contents
    (cons type-tag contents)))

(define (type-tag datum)
  (cond ((number? datum) 'scheme-number)
        ((pair? datum) (car datum))
        (else "Bad tagged datum -- TYPE-TAG" datum)))

(define (contents datum)
  (cond ((number? datum) datum)
        ((pair? datum) (cdr datum))
        (else "Bad tagged datum - CONTENTS" datum)))

; ex-2.79
(define (equ? x y) (apply-generic 'equ? x y))

#|
(define sn1 (make-scheme-number 1))
(define sn2 (make-scheme-number 3))
(define sn3 (make-scheme-number 3))
(print (equ? sn1 sn2))
(print (equ? sn2 sn3))

(define r1 (make-rational 1 2))
(define r2 (make-rational 1 1))
(define r3 (make-rational 1 1))
(print (equ? r1 r2))
(print (equ? r2 r3))

(define c1 (make-complex-from-real-imag 1 2))
(define c2 (make-complex-from-real-imag 1 1))
(define c3 (make-complex-from-real-imag 1 1))
(print (equ? c1 c2))
(print (equ? c2 c3))

; ex-2.80

(define (zero? x) (apply-generic 'zero? x))
(define sn1 (make-scheme-number 1))
(define sn2 (make-scheme-number 0))
(print (zero? sn1))
(print (zero? sn2))

(define r1 (make-rational 1 0))
(define r2 (make-rational 0 2))
(print (zero? r1))
(print (zero? r2))

(define c1 (make-complex-from-real-imag 1 0))
(define c2 (make-complex-from-real-imag 0 1))
(define c3 (make-complex-from-real-imag 0 0))
(print (zero? c1))
(print (zero? c2))
(print (zero? c3))
|#

; 2.5.2 異なる型のデータの統合
(define (add-complex-to-schemenum z x)
  (make-from-real-imag (+ (real-part z) x)
                       (imag-part z)))

(put 'add '(complex scheme-number)
     (lambda (z x) (tag (add-complex-to-schemenum z x))))

(define (scheme-number->complex n)
  (make-complex-from-real-imag (contents n) 0))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; put-coercion,get-coercion for SICP 2.5.2
; https://gist.github.com/kinoshita-lab/b76a55759a0d0968cd97
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define coercion-list '())

(define (clear-coercion-list)
  (set! coercion-list '()))

(define (put-coercion type1 type2 item)
  (if (get-coercion type1 type2) coercion-list
    (set! coercion-list
      (cons (list type1 type2 item)
            coercion-list))))

(define (get-coercion type1 type2)
  (define (get-type1 listItem)
    (car listItem))
  (define (get-type2 listItem)
    (cadr listItem))
  (define (get-item listItem)
    (caddr listItem))
  (define (get-coercion-iter list type1 type2)
    (if (null? list) #f
      (let ((top (car list)))
        (if (and (equal? type1 (get-type1 top))
                 (equal? type2 (get-type2 top))) (get-item top)
          (get-coercion-iter (cdr list) type1 type2)))))
  (get-coercion-iter coercion-list type1 type2))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(put-coercion 'scheme-number 'complex scheme-number->complex)

(define (apply-generic op . args)
  ; (print op)
  ; (print args)
  (let ((type-tags (map type-tag args)))
    (let ((proc (get op type-tags)))
      ; (print proc)
      (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)))
              ; (print t1->t2)
              ; (print t2->t)
              (cond (t1->t2
                      ; (print "hoge")
                      (apply-generic op (t1->t2 a1) a2))
                    (t2->t1
                      ; (print "fuga")
                      (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)))))))

; ex-2.81
(define (scheme-number->scheme-number n ) n)
(define (complex->complex z) z)
(put-coercion 'scheme-number 'scheme-number scheme-number->scheme-number)
(put-coercion 'complex 'complex complex->complex)
(define (exp x y) (apply-generic 'exp x y))

; a.
(define c1 (make-complex-from-real-imag 1 2))
(define c2 (make-complex-from-real-imag 1 1))
; (exp c1 c2)
; procが常に#fなので無限ループに陥る

; b.
; 正しくない, このままだと動かない

; c.
(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)))
            (if (eq? type1 type2) ; 追加
              (error "eq type1 type2") ; 追加
              (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)))))))

; (exp c1 c2)
; *** ERROR: eq type1 type2

; ex-2.82
(define (apply-generic op . args)
  (define (coercion args my-type)
    (if (null? args)
      ()
      (let ((a1 (car args)))
        (let ((t2->t1 (get-coercion (type-tag a1) my-type)))
          ; (print "t2->t1 " t2->t1)
          (if t2->t1
            (cons (t2->t1 a1) (coercion (cdr args) my-type))
            (cons a1 (coercion (cdr args) my-type)))))))

  (define (search types)
    ; (print "---------------------------")
    (let ((my-type (car types)))
      ; (print my-type)
      (let ((my-args (coercion args my-type)))
        ; (print "my-args " my-args)
        (let ((proc (get op (map type-tag my-args))))
          ; (print "proc " proc)
          (if proc
            (apply proc (map contents my-args))
            (search (cdr types)))))))

  ; (print "types" (map type-tag args))
  (search (map type-tag args)))

(put 'add '(scheme-number scheme-number scheme-number)
     (lambda (x y z) (+ x y z)))

(put 'add '(complex complex complex)
     (lambda (x y z) (add (add (cons 'complex x)
                               (cons 'complex y))
                          (cons 'complex z))))

(define (add . args)
  (apply apply-generic (cons 'add args)))

(define z (make-complex-from-real-imag 3 4))

(put-coercion 'scheme-number 'complex scheme-number->complex)

; (print (add z 2 2))
; (complex rectangular 7 . 4)

; ex-2.83
(define (raise x) (apply-generic 'raise x))

;; 実数算術演算パッケージ
(define (install-real-package)
  (define (tag x)
    (print "real-tag")
    (attach-tag 'real x))
  (put 'add '(real real)
       (lambda (x y) (tag (+ x y))))
  (put 'sub '(real real)
       (lambda (x y) (tag (- x y))))
  (put 'mul '(real real)
       (lambda (x y) (tag (* x y))))
  (put 'div '(real real)
       (lambda (x y) (tag (/ x y))))
  (put 'equ? '(real real)
       (lambda (x y) (= x y)))
  (put '=zero? '(real)
       (lambda (x) (= x 0.0)))
  (put 'make 'real
       (lambda (x) (tag x)))


  (put 'raise '(real)
       (lambda (x) (make-complex-from-real-imag x 0)))

  ; ex-2.85追加
  (put 'project 'real
       (lambda (x)
         (let ((rat (rationalize
                      (inexact->exact x) 1/100)))
           (make-rational
             (numerator rat)
             (denominator rat)))))

  'done)

(define (make-real n)
  ((get 'make 'real) n))

(install-real-package)

; (print (raise 1))
; (print (raise (make-rational 1 2)))
; (print (raise (make-real 1.5)))

; (define i 2)
; (print (raise i))
; (print (raise (raise i)))
; (print (raise (raise (raise i))))

; ex-2.84
(define (higher-type x y)
  (let ((tower '(complex real rational scheme-number))) ;; 型の塔
    (define (iter twr)
      (if (null? twr)
        #f
        (cond ((eq? x (car twr)) x)
              ((eq? y (car twr)) y)
              (else (iter (cdr twr))))))
    (iter tower)))

(define (coerce-higher-type items)
  (let ((item1 (car items))
        (item2 (cadr items)))
    (let ((type1 (type-tag item1))
          (type2 (type-tag item2)))
      (if (eq? type1 type2)
        items
        (let ((tag (higher-type type1 type2)))
          (if (eq? tag type1)
            (coerce-higher-type (list item1 (raise item2)))
            (coerce-higher-type (list (raise item1) item2))))))))

(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)))
            (if (eq? type1 type2)
              (error "E1. No method for these types" (list op type-tags))
              (let ((coerced-args (coerce-higher-type args)))
                (let ((proc (get op (map type-tag coerced-args))))
                  (if proc
                    (apply proc (map contents coerced-args))
                    (error "E2.No method for these types" (list op type-tags)))))))
          (error "E3. No method for these types" (list op type-tags)))))))

; ex-2.85
(define (drop x)
  (print "******************************")
  (print "*******drop " x)
  (let ((project-proc (get 'project (type-tag x))))
    (if project-proc
      (let ((project-number (project-proc (contents x))))
        (print "project-number " project-number)
        (print "raise-project-number " (raise project-number))

        (if (equ? project-number (raise project-number))
          (drop project-number)
          x))
      x)))

(define (apply-generic op . args)
  (print "**********apply " op args)

  (let ((type-tags (map type-tag args)))
    (let ((proc (get op type-tags)))
      (print "proc " proc)
      (if proc
        ; (apply proc (map contents args)) ;; drop
        (drop (apply proc (map contents args))) ;; drop
        (if (= (length args) 2)
          (let ((type1 (car type-tags))
                (type2 (cadr type-tags)))
            (if (eq? type1 type2)
              (error "E1. No method for these types" (list op type-tags))
              (let ((coerced-args (coerce-higher-type args)))
                (let ((proc (get op (map type-tag coerced-args))))
                  (if proc
                    (drop (apply proc (map contents coerced-args))) ;; drop
                    (error "E2.No method for these types" (list op type-tags)))))))
          (error "E3. No method for these types" (list op type-tags)))))))

(define rat (make-rational 2 4))
(define rel (make-real 3.0))
(define cpx (make-complex-from-real-imag 2 0))

; (print (drop rat))
; (print (drop cpx))
; (print (add rat rel))
; (print (add cpx rel))

; the tower
; raise ↑
; project ↓
;
; 複素数(complex)
; ↑
; 実数(real)
; ↑
; 有理数(rational)
; ↑
; 整数(scheme-number)