Home > database >  Rb: How does the enumerable module works by just adding an each method on a class in Ruby?
Rb: How does the enumerable module works by just adding an each method on a class in Ruby?

Time:08-19

I'm learning ruby and while studding modules I found that for making Enumerable module methods accessible inside a class you should also provide a each method inside your class. I don't really understand why doing this is making enumerable methods work.

Also, i don't understand why i don't need to call the each method please be kind I'm just beginning to learn

class List 
  include Enumerable
  
  def initialize (*list)
    @list = *list
  end

  def each(&block)
    @list.each(&block)
  end
end

Mylist = List.new(1,2,3,4,5)

puts Mylist.any? {|num| num>2} 

I'm planning to create my own Enumerable module whit some of the Enumerable methods and I think understanding this concept will help me on doing it.

CodePudding user response:

Module Enumerable is designed such way

Every Enumerable method uses each method which yields successive members of the collection. That's why you just need implement it and include Enumerable in your class

If you will use max, min, or sort methods, you also need to implement <=> method

CodePudding user response:

Think of each enumerable method as having as its first line enum = each, which is the same as enum = self.each.

For example, suppose an enumerable method is defined as follows.

module Enumerable
  def reverse_em
    enum = each
    a = []
    loop do
      a.unshift(enum.next)
    end
    a
  end
end

As each in enum = each has no explicit receiver the implicit receiver is used. In [1, 2, 3].reverse_em, for example, the implicit receiver is [1, 2, 3] so enum = each evaluates to enum = [1, 2, 3].each #=> #<Enumerator: [1, 2, 3]:each>, each being an instance method of the class [1, 2, 3].class #=> Array. Here enum.next #=> 1, enum.next #=> 2, enum.next #=> 3, enum.next #=> StopIteration (an exception).

We may then write, for example:

[1, 2, 3].reverse_em
  #=> [3, 2, 1]
{ a: 1, b: 2, c: 3 }.reverse_em
  #=> [[:c, 3], [:b, 2], [:a, 1]]
(1..3).reverse_em
  #=> [3, 2, 1]
require 'set'
st = [1, 2, 3].to_set
st.reverse_em
  #=> [3, 2, 1]

As you see, a single Enumerable method is used by instances of four different classes. This obviously is a superior design construct to defining reverse_em for each of the four classes.

To implement this functionality each of the class definitions for Array, Hash, Range and Set (and other core classes) must have a statement include Enumerable and must contain an instance method each that returns an enumerator (Array#each, Hash#each, Range#each and Set#each).

Note that my method reverse_em returns an array regardless of the receiver's class. That is the case with many if not all Emumerable methods.

If follows that a user's custom class can make use of all of the methods in the Enumerable module by including in the class definition the statement include Enumerable and an instance method each that returns an enumerator. Note that a few Enumerable methods depend on methods other than each that are to be provided by the class.

  • Related