HOME ゲストブック 書き込み一覧 返事を書く

2528. call/ccを用いた不可思議な再帰コード集


亀田馬志 (Dec 01, 2009)

;;; 元ネタは「もうひとつのScheme入門」
;;; http://www.shido.info/lisp/scheme7.html
;;; より。

;; [code 1] 階乗を求める手続き fact
(define (fact n)
  (call/cc
   (lambda (break)
     (* (if (= n 1)
            (break 1)
            n) (fact (- n 1))))))

;; 実行例
;; > (fact 5)
;; 120

;;; 練習問題1

;; 1.リストの要素の数を数える手続き my-length
(define (my-length ls)
  (call/cc
   (lambda (break)
     (+ 1 (my-length (cdr (if (null? ls)
                              (break 0)
                              ls)))))))

;; 2.数のリストの要素の合計を求める手続き my-sum
(define (my-sum ls)
  (call/cc
   (lambda (break)
     (+ (car (if (null? ls)
                 (break 0)
                 ls)) (my-sum (cdr ls))))))

;; 3.リスト (ls) から要素 (x) を取り除いたリストを返す手続き
(define (remove x ls)
  (call/cc
   (lambda (exterior-break)
     (let ((h (car (if (null? ls)
                       (exterior-break '())
                       ls))))
       ((lambda (y) (call/cc
                (lambda (interior-break)
                  (cons (if (eqv? x h)
                            (interior-break y)
                            h) y))))
        (remove x (cdr ls)))))))

;; 4.リスト (ls) の要素 (x) の位置を返す手続き。位置は 0 から数え始め、 x がない場合は #f を返す。
(define (position x ls)
  (position-aux x ls 0))

(define (position-aux x ls i)
  (call/cc
   (lambda (break)
     (position-aux (if (eqv? x (car (if (null? ls)
                                        (break #f)
                                        ls)))
                       (break i)
                       x) (cdr ls) (+ 1 i)))))
                                
;; リストの要素を全て2倍にする関数
(define (list*2 ls)
  (call/cc
   (lambda (break)
     (cons (* 2 (car (if (null? ls)
                         (break '())
                         ls)))
           (list*2 (cdr ls))))))

;; [code 2] 末尾再帰版階乗計算手続き fact-tail
(define (fact-tail n)
  (fact-rec n n))

(define (fact-rec n p)
  (call/cc
   (lambda (break)
     (let ((m (- (if (= n 1)
                     (break p)
                     n) 1)))
       (fact-rec m (* p m))))))

;; 実行例
;; > (fact-tail 5)
;; 120

;;; 練習問題2

;; 1.リストの要素の順番を反転させる手続き my-reverse
(define (my-reverse ls)
  (my-reverse-rec ls '()))

(define (my-reverse-rec ls0 ls1)
  (call/cc
   (lambda (break)
     (my-reverse-rec (cdr (if (null? ls0)
                              (break ls1)
                              ls0)) (cons (car ls0) ls1)))))

;; 2.数のリストの要素の合計を求める手続き my-sum-tail
(define (my-sum-tail ls)
  (my-sum-rec ls 0))

(define (my-sum-rec ls n)
  (call/cc
   (lambda (break)
     (my-sum-rec (cdr (if (null? ls)
                          (break n)
                          ls)) (+ n (car ls))))))

;; 3.正の整数を表す文字列を整数に変換する手続き my-string->integer
(define (my-string->integer str)
  (char2int (string->list str) 0))

(define (char2int ls n)
  (call/cc
   (lambda (break)
     (char2int (cdr (if (null? ls)
                        (break n)
                        ls)) (+ (- (char->integer (car ls)) 48)
                                (* n 10))))))

;; [code 3] named-let での例
(define (fact-let n)
  (call/cc
   (lambda (break)
     (let loop ((n1 n) (p n))
       (let ((m (- (if (= n1 1)
                       (break p)
                       n1) 1)))
         (loop m (* p m)))))))

;;; 練習問題3

;; 1a. remove
(define (remove x ls)
  (call/cc
   (lambda (break)
     (let loop ((ls0 ls) (ls1 '()))
       (loop (cdr (if (null? ls0)
                      (break (reverse ls1))
                      ls0))
             (if (eqv? x (car ls0))
                 ls1
                 (cons (car ls0) ls1)))))))

;; 1b. position
(define (position x ls)
  (call/cc
   (lambda (break)
     (let loop ((ls0 ls) (i 0))
       (loop (cdr (cond ((null? ls0) (break #f))
                        ((eqv? x (car ls0)) (break i))
                        (else ls0))) (+ 1 i))))))

;; 2a. my-reverse-let
(define (my-reverse-let ls)
  (call/cc
   (lambda (break)
     (let loop ((ls0 ls) (ls1 '()))
       (loop (cdr (if (null? ls0)
                      (break ls1)
                      ls0)) (cons (car ls0) ls1))))))

;; 2b. my-sum-let
(define (my-sum-let ls)
  (call/cc
   (lambda (break)
     (let loop ((ls0 ls) (n 0))
       (loop (cdr (if (null? ls0)
                      (break n)
                      ls0)) (+ (car ls0) n))))))

;; 2c. my-string->integer-let
(define (my-string->integer-let str)
  (call/cc
   (lambda (break)
     (let loop ((ls0 (string->list str)) (n 0))
       (loop (cdr (if (null? ls0)
                      (break n)
                      ls0))
             (+ (- (char->integer (car ls0)) 48)
                (* n 10)))))))

;; 3.0 から n 未満の整数のリストを返す手続き range
(define (range n)
  (call/cc
   (lambda (break)
     (let loop ((i 0) (ls1 '()))
       (loop (+ 1 (if (= i n)
                      (break (reverse ls1))
                      i)) (cons i ls1))))))

;; 4.任意個の引数をとりそれらの平均を返す関数
(define (ave . ls)
  (call/cc
   (lambda (break)
     (let loop ((sum 0) (ls1 ls))
       (loop (+ sum (car (if (null? ls1)
                             (break (/ sum (length ls)))
                             ls1))) (cdr ls1))))))

;; 実行例
;; > (ave 1.1 2.3 4.6)
;; 2.6666666666666665
;; > (ave 3.3 4.7 10.2 20.6 100.1)
;; 27.779999999999994

元ねた: