From: Alex Baranosky on 24 Mar 2010 23:35 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/.
From: David Masover on 25 Mar 2010 00:03 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? 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 { ... }
From: Rob Biedenharn on 25 Mar 2010 00:07 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/. > 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(a)AgileConsultingLLC.com
From: Alex Baranosky on 25 Mar 2010 00:37 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/.
From: Alex Baranosky on 25 Mar 2010 02:43
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/. |