LoginSignup
14
13

More than 5 years have passed since last update.

Ruby1.9のイテレータについて勉強した

Last updated at Posted at 2012-11-23

数年間ずっとPython一筋だったけど、Rubyに手を出している。
Pythonの一番のウリはiterator/generatorだと勝手に思う。
そこで、Rubyでiteratorと呼ばれているモノは何なのか、自分なりにやってみた。
勿論RubyにはRubyならではの書きやすいやり方、考え方があるはずだけど。

渡す側

ブロックを渡すだけなら{...}とdo ... endを使っていればよい

(1..3).map {|x| x * 2} # => [2, 4, 6]

ブロック渡さずにnext()とかしたい場合はEnumeratorオブジェクトに変換する

a = (1..3).to_enum
a.next() # => 1

渡すブロックを部品化したい場合

はじめに思いつくこと

def f1(x)
  x * 2
end
(1..3).map {|x| f1(x)} # => [2, 4, 6]

lambdaやProcを使うと、ブロックをオブジェクトにする

f1 = lambda {|x| x * 2}  # f1とf2は同じ意味。f1とf3はスコープの考えがちょっと違う(割愛)
f2 = ->(x) {x * 2}
f3 = Proc.new {|x| x * 2}
(1..3).map &f1 # => [2, 4, 6]
(1..3).map &f2 # => [2, 4, 6]
(1..3).map &f3 # => [2, 4, 6]

渡される側

yieldキーワードを使う

def itr_func
  (1..3).each {|x| yield x}
end
l = []
itr_func {|x| l << x}
l # => [1, 2, 3]

enumeratorオブジェクトにする

無限リストとかに使う

enum = Enumerator.new do |yielder|
  yielder.yield 1
  yielder.yield 2
  yielder.yield 3
end
enum.next() # => 1
enum.next() # => 2
enum.next() # => 3

また、既存のイテレータを変更するのにも使う

def itr_func
  (1..3).each {|x| yield x}
end
enum = Enumerator.new do |yielder|
  Object.itr_func {|x| yielder.yield x}
end
enum.next() # => 1
enum.next() # => 2

続けて応用

ブロックをメソッドチェインみたいに使えるの?

コンテナを渡すのは素直にできる

enum = ->(x){x * 2}
(1..3).map(&enum).map(&enum) # => [4, 8, 12]

コンテナじゃなくて任意のenumerator(Pythonで言うgenerator)を繋げたい。
つまり、あるイテレータにブロックを渡すと、そのブロックに処理を委譲して、その結果をまた移譲するようなenumeratorを返して欲しい。

class Chainer
  attr_reader :enum
  def initialize(enum)
    @enum = enum
  end
  def chain(&block)
    next_enum = Enumerator.new do |yielder|
      loop do
        yielder.yield block.call(enum.next)
      end
    end
    Chainer.new(next_enum)
  end
end

enum = Enumerator.new do |yielder|
  yielder.yield 1
  yielder.yield 2
  yielder.yield 3
end

proder = ->(x) {x * 2}

Chainer.new(enum).chain(&proder).chain(&proder).enum.to_a  # => [4, 8, 12]

無理やり作ってみたけど、きっともっといい方法があるはず。
誰か教えてください。

これとは違う例だけど、ピッケル本には色々やりたいならEnumerator自体を拡張すればいいじゃんとか書いてあった。
さすがにそれはキモい。
外側からくるんであげるのが最もお行儀の良い方法だと思うの。

結論

  • Pythonのgenerator最高
  • Rubyだと配列とかハッシュとか作りまくって渡さないと発狂しそうになる。速度とかキニスンナ。
14
13
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
13