Hmm... lets see...
irb(main):018:0> i = 10.0/0
=> Infinity
irb(main):019:0> r = 0..i
=> 0..Infinity
irb(main):020:0> r.member?(3)
And she hangs
irb(main):004:0> i=1.0/0
=> Infinity
irb(main):005:0> r=0..i
=> 0..Infinity
irb(main):006:0> r.include?(3)
=> true
irb(main):007:0> r.member?(3)
<< hangs >>
^CIRB::Abort: abort then interrupt!!
from /usr/local/lib/ruby/1.8/irb.rb:81:in `irb_abort'
from /usr/local/lib/ruby/1.8/irb.rb:241:in `signal_handle'
from /usr/local/lib/ruby/1.8/irb.rb:66:in `start'
from /usr/local/lib/ruby/1.8/irb.rb:65:in `call'
from (irb):7:in `member?'
from (irb):7
According to PickAxe v1, both include? and member? come from Enumerable, and
are just aliases for each other. Clearly this is not the case in 1.8.x any
more.
Looking at the source, I see that Range#member? iterates using the 'each'
function, whereas Range#include? just compares against the start and end
values.
I think it's a bug that Range#member? doesn't do a 'break' when it finds a
match. OTOH, I guess it's pretty dubious to be relying on Enumerable-type
methods over an infinite enumeration
Anyway, on to a quick implementation. 'Infinity' printed by Float#to_s is
not actually available as a constant:
irb(main):002:0> 1.0/0
=> Infinity
irb(main):003:0> Infinity
NameError: uninitialized constant Infinity
from (irb):3
but I'm going to break normal class encapsulation by defining it as one at
the top level anyway, instead of as Range::Infinity. Simple listing attached
below.
I think it could work, but she needs some tuning. The idea that infinity is a
Float is a bit odd too. Probably should be it's own class. Any one see
otherwise?
It just needs to be any unique sentinel value (or pair of values, for
Infinity and -Infinity). I thought initially that it should be a class, or
two different instances of a class (one instance for +Inf, one for -Inf).
But then again, since the Float infinity has the properties we need, why not
just re-use it? Plus it has useful Comparable properties.
irb(main):001:0> i = 1.0/0
=> Infinity
irb(main):002:0> 3 < i
=> true
irb(main):003:0> 3 > i
=> false
irb(main):004:0> 3 < -i
=> false
irb(main):005:0> 3 > -i
=> true
Actually, this means that Range with infinite limits works already, without
any extra code! But since not everything is necessarily directly comparable
to a Float infinity
If this went into the core, there would be some syntactic sugar which could
be done: e.g. (1..) becomes a synonym for (1..Infinity)
Regards,
Brian.
----- 8< --------------------------------------------------------------
Infinity = 1.0/0
# The test cases below pass WITHOUT adding this extra code! But it
# would be useful for ranges whose elements are not directly comparable
# to Infinity using <, >, <= etc.
class Range
def include?(val)
if first == -Infinity
if last == Infinity
true
elsif exclude_end?
val < last
else
val <= last
end
elsif last == Infinity
val >= first
else
super
end
end
end
if __FILE__ == $0
require 'test/unit'
class InfTest < Test::Unit::TestCase
def testme
a = (-Infinity..-3)
assert_equal(true, a.include?(-Infinity))
assert_equal(true, a.include?(-4))
assert_equal(true, a.include?(-3))
assert_equal(false, a.include?(-2))
assert_equal(false, a.include?(Infinity))
a = (-Infinity...-3)
assert_equal(true, a.include?(-Infinity))
assert_equal(true, a.include?(-4))
assert_equal(false, a.include?(-3))
assert_equal(false, a.include?(-2))
assert_equal(false, a.include?(Infinity))
a = (-3..Infinity)
assert_equal(false, a.include?(-Infinity))
assert_equal(false, a.include?(-4))
assert_equal(true, a.include?(-3))
assert_equal(true, a.include?(-2))
assert_equal(true, a.include?(Infinity))
a = (-Infinity..Infinity)
assert_equal(true, a.include?(-Infinity))
assert_equal(true, a.include?(-4))
assert_equal(true, a.include?(-3))
assert_equal(true, a.include?(-2))
assert_equal(true, a.include?(Infinity))
a = (-3..-2)
assert_equal(false, a.include?(-Infinity))
assert_equal(false, a.include?(-4))
assert_equal(true, a.include?(-3))
assert_equal(true, a.include?(-2))
assert_equal(false, a.include?(Infinity))
end
end
end