私はいくつかの変種を書きました。最初にあなたは
(define (each-fib fn)
(letrec
((next (lambda (a b)
(fn a)
(next b (+ a b)))))
(next 0 1)))
短く書くことができます。このパターンは頻繁に使用されるため、と呼ばれる特別な構文named let
が導入されています。名前付きletを使用すると、関数は次のようになります。
(define (each-fib fn)
(let next ([a 0] [b 1])
(fn a)
(next b (+ a b))))
ある関数から別の関数に制御を流すために、TCOをサポートする言語では継続渡しスタイルを使用できます。各関数は、しばしばk(継続用)と呼ばれる追加の引数を取得します。関数kは、次に何をするかを表します。
このスタイルを使用すると、次のようにプログラムを作成できます。
(define (generate-fibs k)
(let next ([a 0] [b 1] [k k])
(k a (lambda (k1)
(next b (+ a b) k1)))))
(define (count-down n k)
(let loop ([n n] [fibs '()] [next generate-fibs])
(if (zero? n)
(k fibs)
(next (λ (a next)
(loop (- n 1) (cons a fibs) next))))))
(count-down 5 values)
手動でスタイルを書くのは少し面倒なので、コルーチンを導入すると便利かもしれません。使用しないというあなたのルールを破る私は、新しいフィボナッチ数を繰り返し消費するset!
共有変数を使用することを選択しました。カウントダウンが終了すると、ルーチンは単に値を読み取ります。fibs
generate-fibs
count-down
(define (make-coroutine co-body)
(letrec ([state (lambda () (co-body resume))]
[resume (lambda (other)
(call/cc (lambda (here)
(set! state here)
(other))))])
(lambda ()
(state))))
(define fibs '())
(define generate-fib
(make-coroutine
(lambda (resume)
(let next ([a 0] [b 1])
(set! fibs (cons a fibs))
(resume count-down)
(next b (+ a b))))))
(define count-down
(make-coroutine
(lambda (resume)
(let loop ([n 10])
(if (zero? n)
fibs
(begin
(resume generate-fib)
(loop (- n 1))))))))
(count-down)
そして、あなたが通信スレッドを備えたバージョンを手に入れるボーナス:
#lang racket
(letrec ([result #f]
[count-down
(thread
(λ ()
(let loop ([n 10] [fibs '()])
(if (zero? n)
(set! result fibs)
(loop (- n 1) (cons (thread-receive) fibs))))))]
[produce-fibs
(thread
(λ ()
(let next ([a 0] [b 1])
(when (thread-running? count-down)
(thread-send count-down a)
(next b (+ a b))))))])
(thread-wait count-down)
result)
スレッドバージョンはRacket固有であり、他のバージョンはどこでも実行する必要があります。