From: Brian Candler Date: 2004-10-05T17:36:12+09:00 Subject: Re: Too Many Ways? On Tue, Oct 05, 2004 at 04:00:11AM +0900, trans. (T. Onoma) wrote: > > Or can we use the Float 'Infinity' for this? > > 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