Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Enumerator::Lazy

 Enumerator::Lazy

Presented at SF.rb on August 2, 2016.

Erik Berlin

August 02, 2016
Tweet

More Decks by Erik Berlin

Other Decks in Programming

Transcript

  1. Enumerator::Lazy
    Erik Michaels-Ober
    @sferik

    View Slide

  2. Imperative languages

    do iteration like this:
    int sum = 0;
    for(i = 1; i < 10; i = i + 1) {
    sum = sum + i;
    }

    View Slide

  3. Functional languages

    do iteration like this:
    rec_sum [] = 0
    rec_sum (x:xs) = x + rec_sum xs
    rec_sum [1..9]

    View Slide

  4. Object oriented languages

    (should) do iteration like this:
    sum = 0
    (1..9).each do |i|
    sum += i
    end

    View Slide

  5. Object oriented languages

    (should) do iteration like this:
    sum = (1..9).inject(&:+)

    View Slide

  6. Iterators
    Introduced in CLU by

    Barbara Liskov
    (1975)
    Copied in Ruby by
    Yukihiro Matsumoto
    (1995)

    View Slide

  7. Ruby’s iterator is called
    Enumerator

    View Slide

  8. enum = Enumerator.new do |yielder|
    yielder.yield("sf")
    yielder.yield("dot")
    yielder.yield("rb")
    end

    View Slide

  9. ["sf", "dot", "rb"].each
    ["sf", "dot", "rb"].to_enum
    Enumerator.new(["sf", "dot", "rb"])

    View Slide

  10. enum = Enumerator.new do |yielder|
    n = 0
    loop do
    yielder.yield(n)
    n += 1
    end
    end

    View Slide

  11. fib = Enumerator.new do |yielder|
    a = b = 1
    loop do
    yielder.yield(a)
    a, b = b, a + b
    end
    end

    View Slide

  12. module Enumerable
    def lazy_map(&block)
    Enumerator.new do |yielder|
    return to_enum(__method__) unless block_given?
    each do |n|
    yielder.yield(block.call(n))
    end
    end
    end
    end

    View Slide

  13. module Enumerable
    def lazy_select(&block)
    Enumerator.new do |yielder|
    return to_enum(__method__) unless block_given?
    each do |n|
    yielder.yield(n) if block.call(n)
    end
    end
    end
    end

    View Slide

  14. Ruby 2.0 introduced
    Enumerator::Lazy

    View Slide

  15. What are the first five even
    perfect squares over a thousand?

    View Slide

  16. lazy_integers = (1..Float::INFINITY).lazy
    lazy_integers.collect { |x| x ** 2 }.
    select { |x| x.even? }.
    reject { |x| x < 1000 }.
    first(5)
    #=> [1024, 1156, 1296, 1444, 1600]

    View Slide

  17. What are the first five

    twin primes?

    View Slide

  18. require "prime"
    lazy_primes = Prime.lazy
    lazy_primes.select { |x| (x - 2).prime? }.
    collect { |x| [x - 2, x] }.
    first(5)
    #=> [[3, 5], [5, 7], [11, 13], [17, 19], [29, 31]]

    View Slide

  19. module Enumerable
    def repeat_after_first
    return to_enum(__method__) unless block_given?
    each.with_index do |*val, index|
    index.zero? ? yield(*val) : 2.times { yield(*val) }
    end
    end
    end

    View Slide

  20. require "prime"
    lazy_primes = Prime.lazy
    lazy_primes.repeat_after_first.
    each_slice(2).
    select { |x, y| x + 2 == y }.
    first(5)
    #=> [[3, 5], [5, 7], [11, 13], [17, 19], [29, 31]]

    View Slide

  21. When are the next five

    Friday the 13ths?

    View Slide

  22. require "date"
    lazy_dates = (Date.today..Date.new(9999)).lazy
    lazy_dates.select { |d| d.day == 13 }.
    select { |d| d.friday? }.
    first(10)

    View Slide

  23. Detect whether a text file
    contains a string?
    (without reading the entire file into memory)

    View Slide

  24. lazy_file = File.readlines("/path/to/file").lazy
    lazy_file.detect { |x| x =~ /regexp/ }

    View Slide

  25. Being lazy is efficient.

    View Slide

  26. Being lazy is elegant.

    View Slide

  27. View Slide

  28. Thank you

    View Slide