Slide 1

Slide 1 text

Enumerator::Lazy Erik Michaels-Ober @sferik

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Ruby’s iterator is called Enumerator

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

Ruby 2.0 introduced Enumerator::Lazy

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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]

Slide 17

Slide 17 text

What are the first five
 twin primes?

Slide 18

Slide 18 text

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]]

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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]]

Slide 21

Slide 21 text

When are the next five
 Friday the 13ths?

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Being lazy is efficient.

Slide 26

Slide 26 text

Being lazy is elegant.

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

Thank you