從 Enumerator 看 Ruby 的迭代器

從 Enumerator 看 Ruby 的迭代器

RubyConf TW 2019

888339de9e7a88688b6acb30d33e66cd?s=128

蒼時弦や

July 27, 2019
Tweet

Transcript

  1. Review Ruby s Iterator with Enumerator Photo by Tine Ivanič

    on Unsplash
  2. WEB DEVELOPER GAME DEVELOPER ૵ ࣌ ݭ໵ @elct9620

  3. None
  4. As a Ruby developer, We use #each every day

  5. BUT How it works?

  6. #iterator Photo by Joel Fulgencio on Unsplash

  7. def iterator(&block) yield 1 yield 2 yield 3 end

  8. VALUE rb_block_call(VALUE obj, ID mid, int argc, const VALUE *

    argv, VALUE (*bl_proc) (ANYARGS), VALUE data2) { struct iter_method_arg arg; arg.obj = obj; arg.mid = mid; arg.argc = argc; arg.argv = argv; return rb_iterate(iterate_method, (VALUE)&arg, bl_proc, data2); } The block call is iterate in ruby
  9. def each(&block) @i = 0 yield @i += 1 until

    @i >= 10 end
  10. #loop vs #while

  11. static VALUE loop_i(void) { for (;;) { rb_yield_0(0, 0); }

    return Qnil; } Loop is a method with a block
  12. #enumerator Photo by Glenn Carstens-Peters on Unsplash

  13. [].each # => #<Enumerator: []:each>

  14. Why we need Enumerator?

  15. enum = [1, 2, 3].to_enum enum.next # => 1 enum.next

    # => 2 enum.next # => 3
  16. Enumerator vs Enumerable

  17. class Backpack include Enumerable def initialize(items) @items = items end

    def each(&block) @items.each(&block) end end backpack = Backpack.new([:water, :apple]) backpack.map {}
  18. #generator Photo by m0851 on Unsplash

  19. #to_enum vs Enumerator.new

  20. class List def each(&block) #... end end List.new.to_enum # =>

    #<Enumerator: #<List:0x00007fa490988a78>:each>
  21. class List def pop(&block) #... end end List.new.to_enum(:pop) # =>

    #<Enumerator: #<List:0x00007fa491081fa0>:pop>
  22. If Enumerator.new didn t have target ruby will create a

    Generator
  23. Enumerator.new do |yielder| yielder << 1 yielder << 2 end

  24. Why we need Yielder?

  25. enum = Enumerator.new do yield 1 yield 2 end puts

    enum.to_a # => no block given (yield) (LocalJumpError)
  26. class Yielder def initialize(&block) @proc = block.to_proc end def <<(value)

    @proc.call(value) self end end
  27. class Generator def initialize(&block) @proc = block.to_proc end def each(&_block)

    yielder = Yielder.new { |x| yield x } @proc.call(yielder) end end
  28. #lazy Photo by Kate Stone Matheson on Unsplash

  29. It is hard to figure out it, but useful

  30. class Backpack def each(&block) yield p(1) yield p(2) yield p(3)

    end end backpack = Backpack.new.to_enum backpack.map(&:rect).take(1).to_a backpack.lazy.map(&:rect).take(1).to_a
  31. class Backpack def each(&block) yield p(1) yield p(2) yield p(3)

    end end backpack = Backpack.new.to_enum backpack.map(&:rect).take(1).to_a backpack.lazy.map(&:rect).take(1).to_a
  32. backpack.map(&:rect).take(1).to_a # => 1 # => 2 # => 3

    backpack.lazy.map(&:rect).take(1).to_a # => 1
  33. backpack.take(1).to_a # => 1 backpack.lazy.take(1).to_a # => 1

  34. backpack = Backpack.new.to_enum backpack.lazy.reverse_each.take(1).to_a # => 1 # => 2

    # => 3
  35. And last, let s discuss implement #lazy in Ruby

  36. Thanks