How to create an infinite enumerable of Times?

A

Alex Baranosky

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?
 
D

David Masover

infinite list of Mondays (for example).

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

How can I implement this in Ruby?

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 { ... }
 
R

Rob Biedenharn

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

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
(e-mail address removed)
 
A

Alex Baranosky

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
 
A

Alex Baranosky

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
 
R

Robert Klemme

2010/3/25 Rob Biedenharn said:
require 'date'
class Mondays
=A0include Enumerable

=A0def initialize(starting=3DDate.today)
=A0 =A0@monday =3D starting
=A0 =A0@monday +=3D 1 until @monday.wday =3D=3D 1
=A0end

=A0def succ
=A0 =A0@monday +=3D 7
=A0end

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.

http://gist.github.com/343387

Kind regards

robert

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top