Ruby blocks; extending Enumerable

Naming a code block

Recall how we pass a block to a method:


def dostuff 
  yield(7)
end

puts dostuff {|x| x + 9}

This code passes the block {|x| x + 9} to the method dostuff which calls the block on 7

Below is an alternative way to accomplish exactly the same thing (try it!). The difference, however, is that the block now is now a parameter with a name code. Note the syntax of the parameter: &code since we are passing the address of the block. Also note that instead of yield I now call a method call on the code that makes it execute with the parameter given to call.


def dostuff(&code) 
  code.call(7)
end

puts dostuff {|x| x + 9}

This style of passing blocks to a method is very important since it allows you to re-pass the code to method calls inside the method that initially gets the code. This enables recursion with a block being passed as a parameter to the recursive call.

Unfortunately, there does not seem to be a way to pass two code blocks to a function. It is possible to pass another parameter as a lambda-procedure.

Writing your own class that "implements" Enumerable

Suppose we want to write a class of nested arrays so that we can call methods of Enumerable interface on such arrays. The code below shows the beginning of such an implementation. Your task will be to add more methods to this class.

Enumerable is a module which for the purposes of this problem set is similar to a Java interface: it gives a list of methods that a class supports but does not provide an implementation. Note that Ruby does not mandate that all methods are actually implemented. The initialize method stores whatever is passed to it in the variable @narray. The intended value is an array of arbitrary nesting, as shown in the call to new below. Note that p na just prints the address of the object since no to_s is defined.


class NestedArray 
  include Enumerable # Enumerable is a module, not a class
                     # modules are included, not subclassed
  def initialize obj
    @narray = obj
  end 
end

# testing the new class
na = NestedArray.new([[6,7],[8],[[9, 6]]])
p na
#p na.each {|z| puts (z + z)} # need to define each for this to work

Now we define an each method for the NestedArray. The method is recursive. However, to use recursion, we need to pass the sub-arrays to the recursive call. The private method _each does that, and the public each just calls the private one. In Ruby private and public keywords apply to a group of methods, i.e. all methods defined after "private" and until "public" are private.

Study the code for the recursive call carefully. Note that obj is just a normal array so we can use its each method. Likewise you can use other methods of the sub-arrays in your code so your code would be really not that long.


class NestedArray 
  # defining each for nested arrays 
private
# a private recursive method, iterates over nested arrays
# with the code block in &code
# unfortunately, you can only pass one code parameter
 def _each (obj,&code)
    obj.each do |x| 
      if (x.instance_of? Array) 
        _each(x,&code)
      else 
        code.call(x)
      end
    end
 end
 public 
 # the public each method, calls the recursive one with the 
 # nested arrays storage as the parameter
 def each(&code)
   _each(@narray, &code)
 end    
end

na.each {|y| puts "Here is #{y}"}

CSci 4651 course.