Code Aquarium

minazoko's blog -*- 水底のブログ -*-

(Racket) generatorでFizzBuzz

racketにもgeneratorライブラリがあるので、前回のgaucheのコードを移植……しようとした。しかしracketのgeneratorは汎用ストリームのように使うには、どうにも使いづらかった……。
steramとの相互変換が出来ればもっと楽できそうなんだけど。

#lang racket

(require racket/generator)
(require srfi/1)

(define (circular n s)
  (sequence->repeated-generator
   (cons s (make-list (- n 1) ""))))

(define (giota)
  (infinite-generator
   (let loop ((n 0))
     (yield n)
     (loop (+ n 1)))))

(define (generator->list g n)
  (unfold (λ(a)(zero? (cdr a)))
          (λ(a)((car a)))
          (λ(a)(cons (car a) (- (cdr a) 1)))
          (cons g n)))

(define (gfizzbuzz)
  (let ((g (infinite-generator
            (let loop ((gens (list (giota)
                                   (circular 3 "Fizz")
                                   (circular 5 "Buzz"))))
              (yield (match (map (λ(g)(g)) gens)
                       ((list n "" "") n)
                       ((list _ f b) (string-append f b))))
              (loop gens)))))
    (g)  ; drop 1
    g))


;; usage example
;; (generator->list (gfizzbuzz) 100)

追記 (2014/10/7)

for系syntaxや、シーケンスライブラリ in-xxx を使うともう少し綺麗に書けそう。

#lang racket
(require racket/generator)

(define (circular n s)
  (sequence->repeated-generator
   (cons s (make-list (- n 1) ""))))

(define (generator->list g n)
  (for/list ((i (in-naturals)) #:break (>= i n))
    (g)))

(define (gfizzbuzz)
  (define gens (list (circular 3 "Fizz")
                     (circular 5 "Buzz")))
  (let ((g (generator ()
            (for ((i (in-naturals)))
              (yield (match (map (λ(g)(g)) gens)
                       ((list "" "") i)
                       ((list f "") f)
                       ((list "" b) b)
                       ((list f b) (string-append f b))))))))
    (g)  ; drop 1
    g))

(generator->list (gfizzbuzz) 100)