How to create an infinite enumerable of Times?

I want to be able to have an object extend Enumerable in Ruby to be an
infinite list of Mondays (for example).

So it would yield: March 29, April 5, April 12...... etc

How can I implement this in Ruby?

···

--
Posted via http://www.ruby-forum.com/.

First learn to implement Enumerable, then it should be obvious. All you need
to do is define a #each method. So, for example, here's an implementation of
all numbers:

class PositiveIntegers
  include Enumerable
  def each
    i = 1
    while true
      yield i
      i += 1
    end
  end
end

If you can figure out how to do a similar loop for Mondays, you can do that.

Keep in mind that this kind of thing isn't nearly as cool in practice as you
might like -- for one, a lot of the reasons you'd want an enumerable are not
going to work well. For example, calling .map{...}.each is probably going to
result in an infinite loop, as map returns an array.

I would probably do it like this:

module Foo
  def self.positive_integers
    return enum_for(:positive_integers) unless block_given?
    i = 1
    while true
      yield i
      i += 1
    end
  end
end

So that means you could do this:

Foo.positive_integers { ... }

Or, that enum_for means you could also do this:

Foo.positive_integers.each { ... }

···

On Wednesday 24 March 2010 10:35:16 pm Alex Baranosky wrote:

infinite list of Mondays (for example).

So it would yield: March 29, April 5, April 12...... etc

How can I implement this in Ruby?

require 'date'
class Mondays
   include Enumerable

   def initialize(starting=Date.today)
     @monday = starting
     @monday += 1 until @monday.wday == 1
   end

   def succ
     @monday += 7
   end

   def each
     place = @monday.dup
     loop do
       yield place
       place += 7
     end
   end
end

new_week = Mondays.new
# => #<Mondays:0x357938 @monday=#<Date: 4910569/2,0,2299161>>

puts new_week
# #<Mondays:0x357938>
# => nil

new_week.each do |day|
   puts day
   break if day > Date.civil(2010,4,30)
end
# 2010-03-29
# 2010-04-05
# 2010-04-12
# 2010-04-19
# 2010-04-26
# 2010-05-03
# => nil

But don't try to call #to_a on that new_week object or you'll find out that an infinite sequence takes quite a long time to iterate.

-Rob

Rob Biedenharn http://agileconsultingllc.com
Rob@AgileConsultingLLC.com

···

On Mar 24, 2010, at 11:35 PM, Alex Baranosky wrote:

I want to be able to have an object extend Enumerable in Ruby to be an
infinite list of Mondays (for example).

So it would yield: March 29, April 5, April 12...... etc

How can I implement this in Ruby?
-- Posted via http://www.ruby-forum.com/\.

Thanks guys, great stuff.

In reply to the first poster, I have a lazy_select, and lazy_map like
this:

module Enumerable
  def lazy_select(&block)
    Enumerator.new do |enum|
      self.each do |value|
        enum.yield(value) if block.call(value)
      end
    end
  end

  def lazy_map(&block)
    Enumerator.new do |enum|
      self.each do |value|
        enum.yield(block.call(value))
      end
    end
  end
end

···

--
Posted via http://www.ruby-forum.com/.

IMHO you are abusing #succ here since it is intended to return the
next element _of the same class_. Either you make it return self
which I would also consider not too safe because the instance does not
change or you change the design and move it to another class.

Here's my generic solution.

Kind regards

robert

···

2010/3/25 Rob Biedenharn <Rob@agileconsultingllc.com>:

On Mar 24, 2010, at 11:35 PM, Alex Baranosky wrote:

I want to be able to have an object extend Enumerable in Ruby to be an
infinite list of Mondays (for example).

So it would yield: March 29, April 5, April 12...... etc

How can I implement this in Ruby?

require 'date'
class Mondays
include Enumerable

def initialize(starting=Date.today)
@monday = starting
@monday += 1 until @monday.wday == 1
end

def succ
@monday += 7
end

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

So far I've come up with:

module LazyEnumerable
  extend Enumerable

  def select(&block)
    lazily_enumerate { |enum, value| enum.yield(value) if
block.call(value) }
  end

  def map(&block)
    lazily_enumerate {|enum, value| enum.yield(block.call(value))}
  end

  def collect(&block)
    map(&block)
  end

  private

  def lazily_enumerate(&block)
    Enumerator.new do |enum|
      self.each do |value|
        block.call(enum, value)
      end
    end
  end
end

class LazyInfiniteDays
  include LazyEnumerable

  attr_reader :day

  def self.day_of_week
    dow = { :sundays => 0, :mondays => 1, :tuesdays => 2, :wednesdays =>
3, :thursdays => 4, :fridays => 5, :saturdays => 6, :sundays => 7 }
    dow.default = -10
    dow
  end

  DAY_OF_WEEK = day_of_week()

  def advance_to_midnight_of_next_specified_day(day_sym)
    year = DateTime.now.year
    month = DateTime.now.month
    day_of_month = DateTime.now.day
    output_day = DateTime.civil(year, month, day_of_month)
    output_day += 1 until output_day.wday == DAY_OF_WEEK[day_sym]
    output_day
  end

  def initialize(day_sym)
    @day = advance_to_midnight_of_next_specified_day(day_sym)
  end

  def each
    day = @day.dup
    while true
      yield day
      day += 7
    end
  end

  def ==(other)
    return false unless other.kind_of? LazyInfiniteDays
    @day.wday == other.day.wday
  end
end

···

--
Posted via http://www.ruby-forum.com/.