みんな大好き FizzBuzz(Ruby, Python)

外部イテレータ FizzBuzz

a = ["Fizz", "Buzz", "FizzBuzz"]
h = {0=>a[2], 3=>a[0], 6=>a[0], 9=>a[0], 12=>a[0], 5=>a[1], 10=>a[1]}

g = Enumerator.new do |y|
  loop.with_index(1) do |_, i|
    y << (h[i % 15] || i.to_s)
  end
end

p g.take(20)
#=>["1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz",
#   "11", "Fizz", "13", "14", "FizzBuzz", "16", "17", "Fizz", "19", "Buzz"]

Ruby では外部イテレータを使うことはあまりないように思いますが、この例のように Ruby でも「無限リスト」を実装することができます。
あと、上ではハッシュを使ったのが工夫してみたところです。それから、Enumerator#with_index って知っていました?

n 番目の値を出力するため、遊んでみました。上にさらにコードを追加します。

class Enumerator
  def [](num)
    first(num).last
  end
end

1.upto(15) {|i| print "#{g[i]} "}
puts
p g[105]

結果。

1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 
"FizzBuzz"

まあこんなことをするのなら、最初から外部イテレータなど使わなければいいので、倒錯していますね(笑)。
 
※参考

 

Python

同等のことを Python でもやってみました。Python は全然慣れていないので、もっと上手くやるような仕方があると思うのですが。リスト処理のところがまったく無様なので、このやり方を採る意味がないですね。内包表記を使ってもっときれいに書けないでしょうか。

def generate():
    i = 1
    while True:
        if i % 15 == 0:
            a = "FizzBuzz"
        elif i % 3 == 0:
            a = "Fizz"
        elif i % 5 == 0:
            a = "Buzz"
        else:
            a = str(i)
        yield a
        i += 1

g = generate()
l = []
for i in range(20):
    l.append(next(g))
print(l)
#=>['1', '2', 'Fizz', '4', 'Buzz', 'Fizz', '7', '8', 'Fizz', 'Buzz',
#   '11', 'Fizz', '13', '14', 'FizzBuzz', '16', '17', 'Fizz', '19', 'Buzz']

ジェネレータ関数を一度変数に入れなければならないというのも美しくないですね。余分な操作のようにも思えます。
 
こうすれば内包表記で書けるか。

g = generate()
print([next(g) for _ in range(20)])