我写了几个变种。首先你问如果
(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 的,其他版本应该可以在任何地方运行。