Let's say I have a range from 0 to 10:
range = 0...10
Three dots mean, that the last value (10) is excluded:
range.include? 10
=> false
Now, is there a similar and elegant way to exclude the first value?
For the above example, this would mean to include all values that are bigger (>
, not >=
) than 0 and smaller than 10.
I have two suggestions for you, they're not very ideal but they're the best I can think of.
First you can define a new method on the Range class that does what you describe. It'd look like this:
class Range
def have?(x)
if x == self.begin
false
else
include?(x)
end
end
end
p (0..10).have?(0) #=> false
p (0..10).have?(0.00001) #=> true
I don't know, I just used a synonym of "include" as a method name, maybe you can think of something better. But that's the idea.
And then you could do something a little more elaborate, and define a method on the Range class that marks a range as one you want to exclude the beginning value of, and then change Range's include?
method to check for that mark.
class Range
def exclude_begin
@exclude_begin = true
self
end
alias_method :original_include?, :include?
def include?(x)
return false if x == self.begin && instance_variable_defined?(:@exclude_begin)
original_include?(x)
end
alias_method :===, :include?
alias_method :member?, :include?
end
p (0..10).include?(0) #=> true
p (0..10).include?(0.00001) #=> true
p (0..10).exclude_begin.include?(0) #=> false
p (0..10).exclude_begin.include?(0.00001) #=> true
Again, you may want a better (more elegant?) name for the method than exclude_begin
, I just chose that since it's consistent with Range's exclude_end?
method.
Edit: I've got another one for you, just because I find this problem so interesting. :P This'll only work in the very latest version of Ruby 1.9, but will allow the following syntax:
(0.exclude..10).include? 0 #=> false
(0.exclude..10).include? 0.00001 #=> true
It uses the same idea as my 2nd suggestion, but stores the "exclusion marker" in the number instead of the range. I have to use Ruby 1.9's SimpleDelegator to accomplish this (numbers on their own can't have instance variables or anything), which is why it won't work in earlier versions of Ruby.
require "delegate"
class Numeric
def exclude
o = SimpleDelegator.new(self)
def o.exclude_this?() true end
o
end
end
class Range
alias_method :original_include?, :include?
def include?(x)
return false if x == self.begin &&
self.begin.respond_to?(:exclude_this?) &&
self.begin.exclude_this?
original_include?(x)
end
alias_method :===, :include?
alias_method :member?, :include?
end
Maybe you could create your own range type.
class FancyRange
def initialize(minimum, maximum, exclusive_minimum, exclusive_maximum)
# insert code here
end
# insert more code here
end