■
;; http://d.hatena.ne.jp/knight_5/20100111/1263197803
ちょっとだけ解説。
(define (f n) (let1 xs '(")") (set-cdr! xs xs) (let/cc r (fold (lambda (x c) (display x) (if (= c 0) (r 0) (- c 1))) n `(: - ,@xs))))) (dotimes (i 64) (f i) (newline))
ポイントは2つ。循環リストを作ることと継続を利用していること。
と言っても、継続もそんなに難しい使い方をしているのではなく、
単に、ループを途中で打ち切るのと同様のことをしているだけ。
循環リスト
「)))))))))))))))))))))))」と繰り返し表示させたい。
十分だと思っ時に止めることができれば完成。
(define (make-cycle seq) (define (loop xs) (if (null? (cdr xs)) (set-cdr! xs seq) (loop (cdr xs)))) (loop seq)) (define seq '(a b c)) ;; l: a -> b -> c -> '()というリスト ;;(make-cycle seq) ;;(print seq) a b c a b c a b c .... ;; どうにか途中で止めたい。
継続(大域脱出)
例えばリストの各要素を掛けた値を求めたい時
(define (product xs) (if (null? xs) 1 (* (car xs) (product (cdr xs)))))
毎回再帰を書くのは面倒なので、リスト操作関数foldを使う。
(define (product2 xs) (fold * 1 xs))
もう少し手続きを改良しよう。無駄な計算をしている。
例えば、途中で0が見つかったら値が0になることはすぐにわかる。(計算を省略したい)
これを適用する時に、普通の再帰で書いた手続きならば条件をひとつ加えてあげればいい。
(define (product* xs) (cond ((null? xs) 1) ((zero? (car xs)) 0) (else (* (car xs) (product* (cdr xs))))))
これをfoldなどで定義した手続きではどうしよう?そこで継続。
(define (product3 xs) (call/cc (lambda (force-return) (fold (lambda (x n) (if (zero? x) (force-return 0) (* x n))) 1 xs)))) ;; jsなどで考えるとこれと同様 ;; for(i=0, i<a.length; i++){ ;; if(a[i] == 0){result=0; break} //このbreakが使いたいだけ ;; result *= a[i] ;; } ;; return result
継続で循環リストの出力を止めることができそうだ。
(define seq '(a b c)) (make-cycle seq) (define (print-n cycle n) (call/cc (lambda (break) (fold (lambda (x c) (cond ((zero? c) (break #f)) (else (display x) (- c 1)))) n cycle)))) (print-n seq 10) ; => abcabcabca#f
循環リスト(無限の出力)を途中で打ちきって(継続)smiley-triangleは完成。
ただし、永遠に出力させるのではなくどこかで止めたい。(継続を使う)
それで、継続を単なる大域脱出として使っている。