How we iterate

@h6165

Abhishek Yadav

ரூபீ ப்ரோக்ராமர்

Co-organizer: Chennai.rb

Iteration/Looping in Ruby

  • loop
  • while
  • for..in
  • Iterators
 

Iterators are more popular than the others

Iteration/Looping in Ruby

An iterator operates on some kind of list,

It expects a block as argument


[1,2,3].each do |x|
  puts (x**2)
end
each is the iterator
[1,2,3] is the list
do..end is the block

each

Iteration/Looping in Ruby

 

        object.method(argument)

 

Follows this familiar method invocation pattern in object oriented programming

[1,2,3] is an object of the Array class.

The Array class has defined the method each.

The each method takes one argument.

each anatomy

Iteration/Looping in Ruby

  • The block-argument here is a little different.
  • The argument we are passing is a block
  • A block is a lump of code. It can be passed around before being executed. Blocks are closures
  • It is similar to Javascript functions

blocks

Iteration/Looping in Ruby

So,

We are creating a lump of code and passing it to the each method as an argument:

 

{ |x| puts(x**2) }

 

The each method can execute it.

blocks

Iteration/Looping in Ruby

 To execute a block, the yield keyword is used. 

 yield 12 will invoke the given block with 12 as argument

 Its very similar to given_block.call(12) 

 Array's each method is implemented in similar way

block needs yield

def foo
  yield 12
end

foo {|x| x**2 }  #=> 144


def bar(&given_block)  # Very similar
  given_block.call(12)
end

bar {|x| x**2 }  #=> 144

Iteration/Looping in Ruby

Array#each and yield

class Array

  # Approximate implementation of each
  def each
    list = self
    i = 0

    loop do
      break if i >= list.length
      
      yield list[i]   # <= given_block.call(list[i])

      i += 1
    end
  end
end

Iteration/Looping in Ruby

  • While defining the block, we specify the argument within pipes
  • Block code can be enclosed within braces or do..end keywords

And x is the argument in both 

do-end

# So,

{ |x| puts x**2 }
# is the same as

do|x|
  puts(x**2)
end

Iteration/Looping in Ruby

  • Block is not an object
  • Block can be represented as a Proc object/lambda

block as object


a = { |x| puts x**2 }        # SyntaxError


a = Proc.new do |x|          # works
  puts x**2
end

a = lambda{ |x| puts x**2 }  # works

a = ->(x){ puts x**2 }       # works

Iteration/Looping in Ruby

  • Iterators are idiomatic Ruby
  • Iterators read better (subjective)
  • Iterators can be monkey patched
  • Lazy enumeration is possible and simple

Are iterators better ?

Iteration/Looping in Ruby

monkey patching

class Array

  def myeach

    return unless block_given?

    arr = self
    len = arr.length
    i = 0

    loop do
      yield arr[i]

      break if arr[i] == nil  # Loop stops at nil

      i += 1
      i = 0  if i >= len
    end
  end

  alias_method :old_each, :each
  alias_method :each, :myeach

end

Iteration/Looping in Ruby

lazy enumeration



range = [1..Float::INFINITY]

range.lazy.map {|x| x**2 }.first(5)  # [1, 4, 9, 16, 25]

@h6165

Abhishek Yadav

ரூபீ ப்ரோக்ராமர்

Co-organizer: Chennai.rb

how we iterate

By Abhishek Yadav

how we iterate

  • 1,094