原始帰納法

今度は原始帰納法.もちろん単なるif文でいけるのだけど,それだとちょっと見栄えが違うので違和感があるのと,一般の分岐ができてしまう普通のif文ではうっかり原始帰納法に依らないコードを書いてしまいそうな気がして.ただエラーチェックはしてないんで,意図的におかしな使い方をされては困る.
使用例:

gosh> (define ichi (S 0))
ichi
gosh> (define ni (S ichi))
ni
gosh> (define san (S ni)) 
san
gosh> (define-PR  ; 自然数の和
        ((plus 0 y) y)
        ((plus (S x) y) (S plus))) ; ここで右の plus は plus(x, y) の意
plus
gosh> (plus ichi ni)
(S (S (S 0)))
gosh> (plus san san)
(S (S (S (S (S (S 0))))))
gosh> (define-PR
        ((times 0 y) 0)
        ((times (S x) y) (plus y times)))
times
gosh> (times ni san)
(S (S (S (S (S (S 0))))))
gosh> (times san 0) 
0
gosh> (define-PR  ; 前者関数は「内部表現」に依らずとも原始帰納法で定義できる
        ((prd 0) 0)
        ((prd (S x)) x))
prd
gosh> (prd 0)
0
gosh> (prd ichi)
0
gosh> (prd ni)  
(S 0)
gosh> (define-PR  ; 自然数の差,負になるなら0
        ((minus-N 0 y) 0)
        ((minus-N x (S y)) (prd minus-N)))
minus-N
gosh> (minus-N san ni)
(S 0)
gosh> (minus-N ni san)
0
gosh> (minus-N ichi ichi)
0

実装:

(define (S x) ; successor
  (list 'S x))

(define (predecessor x) ; (S x) |-> x を行う.プログラマには見せない.
  (cadr x))

(define-syntax define-PR
  (syntax-rules ()
    ((define-PR ((f1 args1 ...) e1) ((f2 args2 ...) e2))
     (define-PR ((f1 args1 ...) () e1) ((f2 args2 ...) () e2)))
    ((define-PR ((f1 arg1 args1 ...) (reversed1 ...) e1)
                ((f2 arg2 args2 ...) (reversed2 ...) e2))
     (define-PR ((f1 args1 ...) (arg1 reversed1 ...) e1)
                ((f2 args2 ...) (arg2 reversed2 ...) e2)))
    ((define-PR ((f1) (reversed1 ...) e1) ((f2) (reversed2 ...) e2))
     (define-PR-aux (f1 (reversed1 ...) e1) (f2 (reversed2 ...) e2) () () ()))))

(define-syntax define-PR-aux
  (syntax-rules (S)
    ((define-PR-aux (f1 (0 reversed1 ...) e1) (f2 ((S x) reversed2 ...) e2)
                    (args1 ...) (args2 ...) ())
     (define-PR-aux (f1 (reversed1 ...) e1) (f2 (reversed2 ...) e2)
                    (xr args1 ...) (x args2 ...) (xr 0 x)))
    ((define-PR-aux (f1 (arg1 reversed1 ...) e1) (f2 (arg2 reversed2 ...) e2)
                    (args1 ...) (args2 ...) (xr ...))
     (define-PR-aux (f1 (reversed1 ...) e1) (f2 (reversed2 ...) e2)
                    (arg1 args1 ...) (arg1 args2 ...) (xr ...)))
    ((define-PR-aux (f1 () e1) (f2 () e2) (args1 ...) (args2 ...) (xr 0 x))
     (define (f1 args1 ...)
       (if (equal? xr 0)
           e1
           (let* ((x (predecessor xr))
                 (f1 (f1 args2 ...))) e2))))))

><