1

I'm reading The Well-Grounded Rubyist and have come across an extra credit challenge to which there is no answer.

class Array
  def my_each
    c = 0
    until c == size
      yield(self[c])
      c += 1
    end
    self
  end
end

An example is given of creating a my_each with my_times

class Array
  def my_each
    size.my_times do |i|
      yield self[i]
    end
    self
  end
end

With the point that many of Ruby's iterators are built on top of each and not the other way around.

Given the above my_each, how could I use it in an implementation of my_times?

To make it clear, an example of a my_times implementation was given before:

class Integer
  def my_times
    c = 0
    until c == self
      yield(c)
      c += 1
    end
    self
  end
end

5.my_times { |e| puts "The block just got handed #{e}." }

So it would seem that the question most certainly implies using my_each in an implementation of my_times.

Jonathan
  • 10,936
  • 8
  • 64
  • 79

4 Answers4

4

To implement my_times using my_each, all you need to do is call my_each on an array that looks like [0, 1, ..., (x - 1)], where x is self (the Integer):

class Integer
  def my_times(&block)
    (0...self).to_a.my_each do |n|
      yield n
    end
    self
  end
end

P.S. If you defined my_each on Enumerable instead of Array (like the "real" each), you could remove to_a from the third line above and iterate directly over the Range, instead of converting the Range to an Array first.

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
  • Good old ranges. I see what you mean regarding Enumerable. Although the question implicitly forbids it. I will check out your solution. I'm hesitant to accept the answer immediately in case there are any more interesting solutions. – Jonathan May 22 '15 at 18:12
3

In order to implement my_times we need an array to send my_each message to. At that point of the book I don't think range is covered so I implemented without using a range. Here is the solution:

require_relative "my_each"
class Integer
  def my_times
    array = Array.new(self)
    c = 0
    array.my_each do
      array[c] = c
      yield(c)
      c += 1
    end
    self
  end
end
2

Edit: I just noticed Jordan used ... instead of .. which generates the correct output; See this answer for more detail on the difference for ranges. I've updated my answer below.

My account is too new and I can't comment on Jordan's solution; I see this was posted about a year ago but I am currently reading through The Well-Grounded Rubyist and wanted to comment on the solution.

I had approached it the same in a similar way as Jordan but found that the output is off compared to; The Well-Grounded Rubyist implementation of my_times which produces:

puts 5.my_times { |i| puts "I'm on iteration # {i}!" }
I'm on iteration 0!
I'm on iteration 1!
I'm on iteration 2!
I'm on iteration 3!
I'm on iteration 4!

Jordan's solution outputs:

puts 5.my_times { |i| puts "I'm on iteration # {i}!" }
I'm on iteration 0!
I'm on iteration 1!
I'm on iteration 2!
I'm on iteration 3!
I'm on iteration 4!
I'm on iteration 5!

I used a magic number to match The Well-Grounded Rubyist output [See Jordan's solution, using ... instead of .. which removes the need for the magic number]

class Integer
  def my_times
    (0..(self-1)).to_a.my_each do |n|
      yield n
    end
    self
  end
end
Community
  • 1
  • 1
dwyd
  • 55
  • 2
  • 9
0

I shortened dwyd's implementation to supply a block rather than using the do..end.

class Integer
  def my_times
    (0...self).to_a.my_each { |i| yield i }
  end
end

Also, I don't think you need to do self-1.

Ben
  • 139
  • 1
  • 2
  • 10