Interval
While Ruby support the Range class out of the box, is does not quite fullfil the role od a real Interval class. For instance, it does not support excluding the front sentinel. This is because Range also tries to do triple duty as a simple Sequence and as a simple Tuple-Pair, thus limiting its potential as an Interval. The Interval class remedies the situation by commiting to interval behavior, and then extends the class’ capabilites beyond that of the standard Range in ways that naturally fall out of that.
Range depends on two methods: succ and #<=>. If numeric ranges were the only concern, those could just as well be #+ and #<=>, but esoteric forms make that unfeasible —the obvious example being a String range. But a proper Interval class requires mathematical continuation, thus the Interval depends on #+ and #<=>, as well as #- as the inverse of #+.
i = Interval.new(1,5) i.to_a #=> [1,2,3,4,5] i = Interval[0,5] i.to_a(2) #=> [0,2,4] i = Interval[1,5] i.to_a(-1) #=> [5,4,3,2,1] i = Interval[1,3] i.to_a(1,2) #=> [1.0,1.5,2.0,2.5,3.0]
- +@
- -@
- ===
- []
- begin
- closed
- degenerate?
- direction
- distance
- each
- end
- eql?
- exclude_begin?
- exclude_end?
- exclude_first?
- exclude_last?
- first
- first_closed
- first_opened
- half_closed
- include?
- last
- last_closed
- last_opened
- length
- max
- member?
- min
- new
- null?
- opened
- reversed
- sentinels
- size
- step
- ~
[ show source ]
# File lib/more/facets/interval.rb, line 89 def self.[]( *args ) self.new( *args ) end
[ show source ]
# File lib/more/facets/interval.rb, line 93 def initialize(first, last, exclude_first=false, exclude_last=false ) raise ArgumentError, "bad value for interval" if first.class != last.class @first = first @last = last @exclude_first = exclude_first @exclude_last = exclude_last @direction = (@last <=> @first) end
Unary shorthands. These return a new interval exclusive of first, last or both sentinels, repectively.
[ show source ]
# File lib/more/facets/interval.rb, line 159 def +@ ; Interval.new(first, last, true, false) ; end
[ show source ]
# File lib/more/facets/interval.rb, line 160 def -@ ; Interval.new(first, last, false, true) ; end
Alias for first
[ show source ]
# File lib/more/facets/interval.rb, line 139 def closed; Interval.new(@first, @last, true, true) ; end
[ show source ]
# File lib/more/facets/interval.rb, line 125 def degenerate? ; @direction == 0 and ! (@exclusive_first or @exclusive_last) ; end
Returns the direction of the interval indicated by +1, 0 or -1.
(1..5).direction #=> 1 (5..1).direction #=> -1 (1..1).direction #=> 0
[ show source ]
# File lib/more/facets/interval.rb, line 136 def direction ; @direction ; end
Returns the length of the interval as the difference between the first and last elements. Returns nil if the sentinal objects do not support distance comparison (distance). TODO: Add n parameter to count segmentations like those produced by each.
[ show source ]
# File lib/more/facets/interval.rb, line 175 def distance @last - @first #if @last.respond_to?( :distance ) # @last.distance( @first ) #else # #self.to_a.length #end end
Iterates over the interval, passing each _n_th element to the block. If n is not given then n defaults to 1. Each _n_th step is determined by invoking +++ or +\-+ n, depending on the direction of the interval. If n is negative the iteration is preformed in reverse form end sentinal to front sentinal. A second parameter, d, can be given in which case the applied step is calculated as a fraction of the interval‘s length times n / d. This allows iteration over the whole interval in equal sized segments.
1..5.each { |e| ... } #=> 1 2 3 4 5 1..5.each(2) { |e| ... } #=> 1 3 5 1..5.each(1,2) { |e| ... } #=> 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
[ show source ]
# File lib/more/facets/interval.rb, line 239 def each(n=1, d=nil) # :yield: return (n < 0 ? @last : @first) if degenerate? # is this right for all values of n ? s = d ? self.length.to_f * (n.to_f / d.to_f) : n.abs raise "Cannot iterate over zero length steps." if s == 0 s = s * @direction if n < 0 e = @exclude_last ? @last - s : @last #e = @exclude_last ? @last.pred(s) : @last t = @exclude_last ? 1 : 0 #while e.cmp(@first) >= t while (e <=> @first) >= t yield(e) e -= s #e = e.pred(s) end else e = @exclude_first ? @first + s : @first #e = @exclude_first ? @first.succ(s) : @first t = @exclude_last ? -1 : 0 #while e.cmp(@last) <= t while (e <=> @last) <= t yield(e) e += s #e = e.succ(s) end end end
Alias for last
Compares two intervals to see if they are equal
[ show source ]
# File lib/more/facets/interval.rb, line 274 def eql?(other) return false unless @first == other.first return false unless @last == other.last return false unless @exclude_first == other.exclude_first? return false unless @exclude_last == other.exclude_last? true end
[ show source ]
# File lib/more/facets/interval.rb, line 115 def exclude_first? ; @exclude_first ; end
[ show source ]
# File lib/more/facets/interval.rb, line 116 def exclude_last? ; @exclude_last ; end
[ show source ]
# File lib/more/facets/interval.rb, line 111 def first ; @first ; end
[ show source ]
# File lib/more/facets/interval.rb, line 152 def first_closed ; Interval.new(@first, @last, false, true) ; end
[ show source ]
# File lib/more/facets/interval.rb, line 154 def first_opened ; Interval.new(@first, @last, true, false) ; end
Returns a new interval with either the first or the last sentinel exclusive. If the parameter is false, the deafult, then the first sentinel is excluded; if the parameter is true, the last sentinel is excluded.
[ show source ]
# File lib/more/facets/interval.rb, line 147 def half_closed(e=false) e ? Interval.new(@first, @last, true, false) : Interval.new(@first, @last, false, true) end
Returns true or false if the element is part of the interval.
[ show source ]
# File lib/more/facets/interval.rb, line 197 def include?(x) # todo: infinity? tf = exclude_first? ? 1 : 0 tl = exclude_last? ? -1 : 0 (x <=> first) >= tf and (x <=> last) <= tl end
[ show source ]
# File lib/more/facets/interval.rb, line 112 def last ; @last ; end
[ show source ]
# File lib/more/facets/interval.rb, line 153 def last_closed ; Interval.new(@first, @last, true, false) ; end
[ show source ]
# File lib/more/facets/interval.rb, line 155 def last_opened ; Interval.new(@first, @last, false, true) ; end
Alias for distance
[ show source ]
# File lib/more/facets/interval.rb, line 192 def max ((@first <=> @last) == 1) ? @first : @last end
[ show source ]
# File lib/more/facets/interval.rb, line 187 def min ((@first <=> @last) == -1) ? @first : @last end
[ show source ]
# File lib/more/facets/interval.rb, line 128 def null? ; @direction == 0 and @exclusive_first and @exclusive_last ; end
[ show source ]
# File lib/more/facets/interval.rb, line 142 def opened; Interval.new(@first, @last, true, true) ; end
[ show source ]
# File lib/more/facets/interval.rb, line 167 def reversed Interval.new(@last, @first, true, true) end
[ show source ]
# File lib/more/facets/interval.rb, line 106 def sentinels return [@first, @last] end
Alias for distance
Alias for each
[ show source ]
# File lib/more/facets/interval.rb, line 161 def ~@ ; Interval.new(first, last, true, true) ; end