Reopening Classes

Can someone offer an explaination as to what I'm encountering here..

......................................................................................
This code doesn't work...

class Time
  def <=>(other_time)
    self.strftime("%H%M%S%Y%m%d").to_i <=>
other_time.strftime("%H%M%S%Y%m%d").to_i
  end
end

first = Time.local(2006, 10, 21)
last = Time.local(2006, 10, 27)

time_slots = []
range = first..last
range.step(1.hour) do |day_slot|
  time_slots << day_slot
end

time_slots.sort

......................................................................................

the time_slots array isn't populating properly
......................................................................................

This does

first = Time.local(2006, 10, 21)
last = Time.local(2006, 10, 27)

time_slots = []
range = first..last
range.step(1.hour) do |day_slot|
  time_slots << day_slot
end

class Time
  def <=>(other_time)
    self.strftime("%H%M%S%Y%m%d").to_i <=>
other_time.strftime("%H%M%S%Y%m%d").to_i
  end
end

time_slots.sort

......................................................................................

Tony

Range#each employs <=>, so by changing the semantics of the
comparison, you break your step loop. When you redefine <=> after the
loop, it doesn't affect it, so your second example works.

Instead of altering Time, you could just do this:

time_slots.sort_by{ |ts| ts.strftime("%H%M%S%Y%m%d").to_i }

Paul.

···

On 25/10/06, anthony.green@bbc.co.uk <anthony.green@bbc.co.uk> wrote:

Can someone offer an explaination as to what I'm encountering here..

i see you already got an answer, but you might want to know about this idiom:

   harp:~ > cat a.rb
   lt = lambda{|*a| Time.local *a}
   hr = 3600
   times =
   fields = %w( hour min sec year month day )

   ( lt[2006, 10, 21] .. lt[2006, 10, 23] ).step(hr){|t| times << t}

   sorted = times.sort_by{|t| fields.map{|f| t.send f}}

   puts sorted.first(16)

   harp:~ > ruby a.rb
   Sat Oct 21 00:00:00 MDT 2006
   Sun Oct 22 00:00:00 MDT 2006
   Mon Oct 23 00:00:00 MDT 2006
   Sat Oct 21 01:00:00 MDT 2006
   Sun Oct 22 01:00:00 MDT 2006
   Sat Oct 21 02:00:00 MDT 2006
   Sun Oct 22 02:00:00 MDT 2006
   Sat Oct 21 03:00:00 MDT 2006
   Sun Oct 22 03:00:00 MDT 2006
   Sat Oct 21 04:00:00 MDT 2006
   Sun Oct 22 04:00:00 MDT 2006
   Sat Oct 21 05:00:00 MDT 2006
   Sun Oct 22 05:00:00 MDT 2006
   Sat Oct 21 06:00:00 MDT 2006
   Sun Oct 22 06:00:00 MDT 2006
   Sat Oct 21 07:00:00 MDT 2006

the basic idea of

   fields = %w( one two three )

   list.sort_by{|elem| fields.map{|f| elem.send f}}

is powerful.

regards.

-a

···

On Wed, 25 Oct 2006, anthony.green@bbc.co.uk wrote:

Can someone offer an explaination as to what I'm encountering here..

......................................................................................
This code doesn't work...

class Time
def <=>(other_time)
   self.strftime("%H%M%S%Y%m%d").to_i <=>
other_time.strftime("%H%M%S%Y%m%d").to_i
end
end

first = Time.local(2006, 10, 21)
last = Time.local(2006, 10, 27)

time_slots =
range = first..last
range.step(1.hour) do |day_slot|
time_slots << day_slot
end

time_slots.sort

......................................................................................

the time_slots array isn't populating properly
......................................................................................

This does

first = Time.local(2006, 10, 21)
last = Time.local(2006, 10, 27)

time_slots =
range = first..last
range.step(1.hour) do |day_slot|
time_slots << day_slot
end

class Time
def <=>(other_time)
   self.strftime("%H%M%S%Y%m%d").to_i <=>
other_time.strftime("%H%M%S%Y%m%d").to_i
end
end

time_slots.sort

......................................................................................

--
my religion is very simple. my religion is kindness. -- the dalai lama

You can use a custom comparison function by passing a block to sort, so
there's no reason to override the spaceship operator (that is, the <=>
operator).

You could use time_slots.sort {|x,y| x.strftime("%H%M%S%Y%m%d").to_i \
<=> y.strftime("%H%M%S%Y%m%d").to_i}

It's probably more efficient to use sort_by, as that only computes each
strftime once:

time_slots.sort_by{|x| x.strftime("%H%M%S%Y%m%d").to_i }

--Ken

···

On Wed, 25 Oct 2006 05:40:51 -0700, "anthony.green wrote:

Can someone offer an explaination as to what I'm encountering here..

......................................................................................
This code doesn't work...

class Time
  def <=>(other_time)
    self.strftime("%H%M%S%Y%m%d").to_i <=>
other_time.strftime("%H%M%S%Y%m%d").to_i
  end
end

first = Time.local(2006, 10, 21)
last = Time.local(2006, 10, 27)

time_slots =
range = first..last
range.step(1.hour) do |day_slot|
  time_slots << day_slot
end

time_slots.sort

......................................................................................

the time_slots array isn't populating properly
......................................................................................

This does

first = Time.local(2006, 10, 21)
last = Time.local(2006, 10, 27)

time_slots =
range = first..last
range.step(1.hour) do |day_slot|
  time_slots << day_slot
end

class Time
  def <=>(other_time)
    self.strftime("%H%M%S%Y%m%d").to_i <=>
other_time.strftime("%H%M%S%Y%m%d").to_i
  end
end

time_slots.sort

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/
I've added a signing subkey to my GPG key. Please update your keyring.

Another observation.

Building a range of times and then using step to obtain each hour is
pretty inefficient.

Range#step basically works like this I might have an off by one bug
but it gets the idea across:

class Range
  def step(incr = 1)
   elt = self.begin
   end = self.end
   ctr = 0
   while ((elt <=> end)
        if (ctr == 0)
             yield elt
             ctr = incr
        end
        elt = elt.succ
        ctr -= 1
    end
end

And Time#succ gives the time one second later than the receiver.

So the while look is going to be executed once for every second in the
range, in this case 6 days * 24 hours * 60 minutes * 60 seconds, so to
get
the 144 Time objects actually needed, a total of 518400 will be allocated.

This costs time and memory, and GC cycles, which may or may not be important.

Another way to compute timeslots which avoids this might be:

   timeslots = (0..6*24).map {|h| first + h.hour}

Generalizing this to arbitray start and end times is left as an
exercise to the reader.

···

On 10/25/06, Paul Battley <pbattley@gmail.com> wrote:

On 25/10/06, anthony.green@bbc.co.uk <anthony.green@bbc.co.uk> wrote:
>
> Can someone offer an explaination as to what I'm encountering here..

Range#each employs <=>, so by changing the semantics of the
comparison, you break your step loop. When you redefine <=> after the
loop, it doesn't affect it, so your second example works.

Instead of altering Time, you could just do this:

time_slots.sort_by{ |ts| ts.strftime("%H%M%S%Y%m%d").to_i }

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Another way to compute timeslots which avoids this might be:

   timeslots = (0..6*24).map {|h| first + h.hour}

Generalizing this to arbitray start and end times is left as an
exercise to the reader.

Something like this ?

# add a to date method for Time class

class Time
  def to_date
    Date.new(year, month, day)
    rescue NameError
    nil
  end
end

# first date and last date

  first = Time.local(2006, 10, 21, 0, 0, 0)
  last = Time.local(2006, 10, 28, 0, 0, 0)

# calculate the number of days between them

    range = last.to_date - first.to_date

# create your array

    timeslots = (0..range*24).map {|h| first + h.hour}

# remove the last one as it'll be the first time slot of the next day

    @timeslots = timeslots[0, timeslots.size - 1]

# sort by timeslot then by day

    @timeslots.sort! {|a, b| a.strftime("%H%M%S%Y%m%d").to_i <=>
b.strftime("%H%M%S%Y%m%d").to_i}

Tony

Another way to compute timeslots which avoids this might be:

   timeslots = (0..6*24).map {|h| first + h.hour}

Generalizing this to arbitray start and end times is left as an
exercise to the reader.

Maybe this ? so you represent day segments

start_time = Time.gm(2006, 10, 31, 0, 0, 0)
slots = 3
no_of_days = 3

schedule =
timeslots = (0..slots).map{|h| start_time + h.hour}
timeslots.each do |timeslot|
  (0..no_of_days).map do |i|
    schedule << timeslot+i.day
  end
end
puts schedule

Tony

http://runt.rubyforge.org/

-a

···

On Fri, 27 Oct 2006, anthony.green@bbc.co.uk wrote:

Another way to compute timeslots which avoids this might be:

   timeslots = (0..6*24).map {|h| first + h.hour}

Generalizing this to arbitray start and end times is left as an
exercise to the reader.

Maybe this ? so you represent day segments

start_time = Time.gm(2006, 10, 31, 0, 0, 0)
slots = 3
no_of_days = 3

schedule =
timeslots = (0..slots).map{|h| start_time + h.hour}
timeslots.each do |timeslot|
(0..no_of_days).map do |i|
   schedule << timeslot+i.day
end
end
puts schedule

Tony

--
my religion is very simple. my religion is kindness. -- the dalai lama

>> Another way to compute timeslots which avoids this might be:
>>
>> timeslots = (0..6*24).map {|h| first + h.hour}
>>
>> Generalizing this to arbitray start and end times is left as an
>> exercise to the reader.
>
> Maybe this ? so you represent day segments
>
> start_time = Time.gm(2006, 10, 31, 0, 0, 0)
> slots = 3
> no_of_days = 3
>
> schedule =
> timeslots = (0..slots).map{|h| start_time + h.hour}
> timeslots.each do |timeslot|
> (0..no_of_days).map do |i|
> schedule << timeslot+i.day
> end
> end
> puts schedule
>
> Tony

http://runt.rubyforge.org/

I remember very well when someone bashed me because I reported a broken
link, I am still wondering why, anyway
Ara is the link ok, I can go to rubyforge but not to runt, or was that
"rant" :wink:
R.

-a

···

On 10/26/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:

On Fri, 27 Oct 2006, anthony.green@bbc.co.uk wrote:
--
my religion is very simple. my religion is kindness. -- the dalai lama

--
The reasonable man adapts himself to the world; the unreasonable one
persists in trying to adapt the world to himself. Therefore all progress
depends on the unreasonable man.

- George Bernard Shaw