Class: Interval
- Includes:
- Enumerable::Arguments, Multiton
- Defined in:
- lib/supplemental/facets/interval.rb
Overview
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]
Class Method Summary collapse
Instance Method Summary collapse
-
#+@ ⇒ Object
Unary shorthands.
- #-@ ⇒ Object
-
#closed ⇒ Object
Returns a new interval inclusive of of both sentinels.
-
#degenerate? ⇒ Boolean
Returns
true
if the start and end sentinels are equal and the interval is closed; otherwisefalse
. -
#direction ⇒ Object
Returns the direction of the interval indicated by +1, 0 or -1.
-
#distance ⇒ Object
(also: #length, #size)
Returns the length of the interval as the difference between the first and last elements.
-
#each(n = 1, d = nil) ⇒ Object
(also: #step)
Iterates over the interval, passing each _n_th element to the block.
-
#eql?(other) ⇒ Boolean
Compares two intervals to see if they are equal.
- #exclude_first? ⇒ Boolean (also: #exclude_begin?)
- #exclude_last? ⇒ Boolean (also: #exclude_end?)
-
#first ⇒ Object
(also: #begin)
Returns the first or last sentinal of the interval.
-
#first_closed ⇒ Object
Returns a new interval with one of the two sentinels opened or closed.
- #first_opened ⇒ Object
-
#half_closed(e = false) ⇒ Object
Returns a new interval with either the first or the last sentinel exclusive.
-
#include?(x) ⇒ Boolean
(also: #===, #member?)
Returns true or false if the element is part of the interval.
-
#initialize(first, last, exclude_first = false, exclude_last = false) ⇒ Interval
constructor
A new instance of Interval.
- #last ⇒ Object (also: #end)
- #last_closed ⇒ Object
- #last_opened ⇒ Object
-
#max ⇒ Object
Returns the greater of the first and last sentinals.
-
#min ⇒ Object
Returns the lesser of the first and last sentinals.
-
#null? ⇒ Boolean
Returns
true
if the start and end sentinels are equal and the interval is open; otherwisefalse
. -
#opened ⇒ Object
Returns a new interval exclusive of both sentinels.
-
#reversed ⇒ Object
Returns a new interval with the sentinels reversed.
-
#sentinels ⇒ Object
Returns a two element array of first and last sentinels.
- #~@ ⇒ Object
Methods included from Enumerable::Arguments
#find, #to_a, wrap_enumerable_method
Methods included from Multiton
Constructor Details
#initialize(first, last, exclude_first = false, exclude_last = false) ⇒ Interval
Returns a new instance of Interval.
95 96 97 98 99 100 101 102 |
# File 'lib/supplemental/facets/interval.rb', line 95 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 |
Class Method Details
.[](*args) ⇒ Object
90 91 92 |
# File 'lib/supplemental/facets/interval.rb', line 90 def self.[]( *args ) self.new( *args ) end |
Instance Method Details
#+@ ⇒ Object
Unary shorthands. These return a new interval exclusive of first, last or both sentinels, repectively.
161 |
# File 'lib/supplemental/facets/interval.rb', line 161 def +@ ; Interval.new(first, last, true, false) ; end |
#-@ ⇒ Object
162 |
# File 'lib/supplemental/facets/interval.rb', line 162 def -@ ; Interval.new(first, last, false, true) ; end |
#closed ⇒ Object
Returns a new interval inclusive of of both sentinels.
141 |
# File 'lib/supplemental/facets/interval.rb', line 141 def closed; Interval.new(@first, @last, true, true) ; end |
#degenerate? ⇒ Boolean
Returns true
if the start and end sentinels are equal and the interval is closed; otherwise false
.
127 |
# File 'lib/supplemental/facets/interval.rb', line 127 def degenerate? ; @direction == 0 and ! (@exclusive_first or @exclusive_last) ; end |
#direction ⇒ Object
Returns the direction of the interval indicated by +1, 0 or -1.
(1..5).direction #=> 1
(5..1).direction #=> -1
(1..1).direction #=> 0
138 |
# File 'lib/supplemental/facets/interval.rb', line 138 def direction ; @direction ; end |
#distance ⇒ Object Also known as: length, size
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.
177 178 179 180 181 182 183 184 |
# File 'lib/supplemental/facets/interval.rb', line 177 def distance @last - @first #if @last.respond_to?( :distance ) # @last.distance( @first ) #else # #self.to_a.length #end end |
#each(n = 1, d = nil) ⇒ Object Also known as: step
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
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
# File 'lib/supplemental/facets/interval.rb', line 241 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 |
#eql?(other) ⇒ Boolean
Compares two intervals to see if they are equal
276 277 278 279 280 281 282 |
# File 'lib/supplemental/facets/interval.rb', line 276 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 |
#exclude_first? ⇒ Boolean Also known as: exclude_begin?
117 |
# File 'lib/supplemental/facets/interval.rb', line 117 def exclude_first? ; @exclude_first ; end |
#exclude_last? ⇒ Boolean Also known as: exclude_end?
118 |
# File 'lib/supplemental/facets/interval.rb', line 118 def exclude_last? ; @exclude_last ; end |
#first ⇒ Object Also known as: begin
Returns the first or last sentinal of the interval.
113 |
# File 'lib/supplemental/facets/interval.rb', line 113 def first ; @first ; end |
#first_closed ⇒ Object
Returns a new interval with one of the two sentinels opened or closed
154 |
# File 'lib/supplemental/facets/interval.rb', line 154 def first_closed ; Interval.new(@first, @last, false, true) ; end |
#first_opened ⇒ Object
156 |
# File 'lib/supplemental/facets/interval.rb', line 156 def first_opened ; Interval.new(@first, @last, true, false) ; end |
#half_closed(e = false) ⇒ Object
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.
149 150 151 |
# File 'lib/supplemental/facets/interval.rb', line 149 def half_closed(e=false) e ? Interval.new(@first, @last, true, false) : Interval.new(@first, @last, false, true) end |
#include?(x) ⇒ Boolean Also known as: ===, member?
Returns true or false if the element is part of the interval.
199 200 201 202 203 204 |
# File 'lib/supplemental/facets/interval.rb', line 199 def include?(x) # todo: infinity? tf = exclude_first? ? 1 : 0 tl = exclude_last? ? -1 : 0 (x <=> first) >= tf and (x <=> last) <= tl end |
#last ⇒ Object Also known as: end
114 |
# File 'lib/supplemental/facets/interval.rb', line 114 def last ; @last ; end |
#last_closed ⇒ Object
155 |
# File 'lib/supplemental/facets/interval.rb', line 155 def last_closed ; Interval.new(@first, @last, true, false) ; end |
#last_opened ⇒ Object
157 |
# File 'lib/supplemental/facets/interval.rb', line 157 def last_opened ; Interval.new(@first, @last, false, true) ; end |
#max ⇒ Object
Returns the greater of the first and last sentinals.
194 195 196 |
# File 'lib/supplemental/facets/interval.rb', line 194 def max ((@first <=> @last) == 1) ? @first : @last end |
#min ⇒ Object
Returns the lesser of the first and last sentinals.
189 190 191 |
# File 'lib/supplemental/facets/interval.rb', line 189 def min ((@first <=> @last) == -1) ? @first : @last end |
#null? ⇒ Boolean
Returns true
if the start and end sentinels are equal and the interval is open; otherwise false
.
130 |
# File 'lib/supplemental/facets/interval.rb', line 130 def null? ; @direction == 0 and @exclusive_first and @exclusive_last ; end |
#opened ⇒ Object
Returns a new interval exclusive of both sentinels.
144 |
# File 'lib/supplemental/facets/interval.rb', line 144 def opened; Interval.new(@first, @last, true, true) ; end |
#reversed ⇒ Object
Returns a new interval with the sentinels reversed.
(0..10).reversed #=> 10..0
169 170 171 |
# File 'lib/supplemental/facets/interval.rb', line 169 def reversed Interval.new(@last, @first, true, true) end |
#sentinels ⇒ Object
Returns a two element array of first and last sentinels.
(0..10).sentinels #=> [0,10]
108 109 110 |
# File 'lib/supplemental/facets/interval.rb', line 108 def sentinels return [@first, @last] end |