From: "trans. (T. Onoma)" Date: 2004-10-06T02:20:05+09:00 Subject: Re: [RCR] New [] Semantics On Tuesday 05 October 2004 07:26 am, Brian Candler wrote: > > def member?(e) > > return ( (((e + self.begin) % @increment) == 0) && self.between?(e) ) > > end > > Erm, no I don't get that. You're declaring a 'member?' test for a class of > objects which support the methods '+', '%', 'between?' and '==' with some > particular semantics (they form a group?? IANAM), but presumably not > numbers. Phew! Okay, I spent the last few hours working this thing out. What a chore! See below for source. It should clear most everything up. > Can you give an example of a class of objects for which your 'member?' > function is useful, but this one is not: > > def member?(e) > return e >= self.begin and e <= self.end > end > #or > def member?(e) > return e.between(self.begin, self.end) > end Well, Numeric is really the only good case. At first I thought that it might work for anything that can be coerced into Numeric having one-to-one transformation and a linear increment. And although it could, I couldn't think of any good use cases besides Numerics. So why fuss with all that? So I didn't. > I also can't see what "x.between?(y)" is supposed to do, with a single > argument (y). A better term is probably #circumscribes? And I use that in my code. T. ---------- Infinity = 1.0/0 # for added flare ;) module Comparable alias old_between? between? def between?(a,b=nil) if a.kind_of?(Range) a.circumscribes?(self) else old_between(a,b) end end end class Range alias init_old initialize def initialize(first, last, exfirst=false, exlast=false, step=1) @exclude_first = exfirst @step = step init_old(first, last, exlast) # not needed, Ruby catches already # if ! numeric_range? && first == -Infinity # raise "Ordinal range can not begin with -Infinity." # end end def exclude_first? @exclude_first end alias exclude_begin? exclude_first? alias exclude_last? exclude_end? def numeric_range? @numeric_range ||= (first.kind_of?(Numeric) && last.kind_of?(Numeric)) end def infinite_range? @infinite_range ||= (first.abs == Infinity or last.abs == Infinity) end def step(s=nil) if s @step = s else @step ||= 1 end @step end def x(s) @step = s self end def member?(val) if numeric_range? return false if ! circumscribes?(val) return true if first == -Infinity && last == Infinity # ? return true if val.abs == Infinity # this one was tricky if first.abs != Infinity return (((val + first) % step) == 0) elsif first == -Infinity # flip this around r = Range.new(-last,Infinity,exclude_last?,exclude_first?,step) return r.member?(-val) else # first == Infinity true end else # ordinal range # looks like this isn't needed as Ruby sees this a bad news already! # # last can't be Infinite too (otherwise it be numeric range) # if first == -Infinity # # should be caught in initialize but I can't trap literal so... # raise "Ordinal range can not begin with -Infinity." # end # basic operation til = exclude_last? ? -1 : 0 elem = exclude_first? ? first.succ : first while (elem <=> last) < til return true if (val <=> elem) == 0 step.times { elem = elem.succ } end end false end alias include? member? def circumscribes?(val) case val<=>first when -1 then return false when 0 then return false if exclude_first? end case val<=>last when 1 then return false when 0 then return false if exclude_last? end return true end end ------------------- # no doubt there are a lot more tests to do ;) require 'succ.succ/range' require 'test/unit' class GeneralTest < Test::Unit::TestCase def test_circumscribes? a = (1..10) assert_equal(false, a.circumscribes?(0)) assert_equal(true, a.circumscribes?(1)) assert_equal(true, a.circumscribes?(2)) assert_equal(true, a.circumscribes?(9)) assert_equal(true, a.circumscribes?(10)) assert_equal(false, a.circumscribes?(11)) a = (1...10) assert_equal(false, a.circumscribes?(0)) assert_equal(true, a.circumscribes?(1)) assert_equal(true, a.circumscribes?(2)) assert_equal(true, a.circumscribes?(9)) assert_equal(false, a.circumscribes?(10)) assert_equal(false, a.circumscribes?(11)) a = Range.new(1,10,true,false) assert_equal(false, a.circumscribes?(0)) assert_equal(false, a.circumscribes?(1)) assert_equal(true, a.circumscribes?(2)) assert_equal(true, a.circumscribes?(9)) assert_equal(true, a.circumscribes?(10)) assert_equal(false, a.circumscribes?(11)) a = Range.new(1,10,true,true) assert_equal(false, a.circumscribes?(0)) assert_equal(false, a.circumscribes?(1)) assert_equal(true, a.circumscribes?(2)) assert_equal(true, a.circumscribes?(9)) assert_equal(false, a.circumscribes?(10)) assert_equal(false, a.circumscribes?(11)) end end class LrgNumericTest < Test::Unit::TestCase def test_include a = (0...100000000) assert_equal(true, a.include?(0)) assert_equal(true, a.include?(1000)) assert_equal(true, a.include?(1000000)) assert_equal(false, a.include?(100000000)) assert_equal(false, a.include?(Infinity)) end def test_include_with_step a = (0..100000000).x 5 assert_equal(true, a.include?(0)) assert_equal(true, a.include?(5)) assert_equal(false, a.include?(70007)) assert_equal(true, a.include?(5000005)) assert_equal(false, a.include?(Infinity)) end end class InfTest < Test::Unit::TestCase def test_include? 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 class OrdinalTest < Test::Unit::TestCase def test_include a = ('a'..'g') assert_equal(false, a.include?(-Infinity)) assert_equal(true, a.include?('a')) assert_equal(true, a.include?('c')) assert_equal(false, a.include?('z')) assert_equal(false, a.include?(Infinity)) end def test_error assert_raises(ArgumentError) { a = (-Infinity..'z') } end end -------------------- Loaded suite range_test Started ...... Finished in 0.024425 seconds. 6 tests, 65 assertions, 0 failures, 0 errors