Class: Interval
- Includes:
- Argumentable, Multiton
- Defined in:
- lib/standard/facets/interval.rb
Overview
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..step(2).to_a #=> [0,2,4]
i = Interval[1,5]
i.step(-1).to_a #=> [5,4,3,2,1]
i = Interval[1,3]
i.step(1,2).to_a #=> [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 = nil, d = nil) ⇒ Object
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.
-
#step(n = 1, d = nil) ⇒ Object
:yield:.
- #~@ ⇒ Object
Methods included from Multiton
Methods included from Multiton::Inclusive
Constructor Details
#initialize(first, last, exclude_first = false, exclude_last = false) ⇒ Interval
Returns a new instance of Interval.
45 46 47 48 49 50 51 52 |
# File 'lib/standard/facets/interval.rb', line 45 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
40 41 42 |
# File 'lib/standard/facets/interval.rb', line 40 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.
111 |
# File 'lib/standard/facets/interval.rb', line 111 def +@ ; Interval.new(first, last, true, false) ; end |
#-@ ⇒ Object
112 |
# File 'lib/standard/facets/interval.rb', line 112 def -@ ; Interval.new(first, last, false, true) ; end |
#closed ⇒ Object
Returns a new interval inclusive of of both sentinels.
91 |
# File 'lib/standard/facets/interval.rb', line 91 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
.
77 |
# File 'lib/standard/facets/interval.rb', line 77 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
88 |
# File 'lib/standard/facets/interval.rb', line 88 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.
128 129 130 131 132 133 134 135 |
# File 'lib/standard/facets/interval.rb', line 128 def distance @last - @first #if @last.respond_to?( :distance ) # @last.distance( @first ) #else # #self.to_a.length #end end |
#each(n = nil, d = nil) ⇒ Object
Deprecate arguments and simplify each definition accordingly.
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
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/standard/facets/interval.rb', line 193 def each(n=nil, d=nil) # :yield: if n warn "FACETS: `interval.each(n,d){...}` will be deprecated.\n" + "Use `interval.step(n,d).each{...}` instead." else n = 1 end 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
269 270 271 272 273 274 275 |
# File 'lib/standard/facets/interval.rb', line 269 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?
67 |
# File 'lib/standard/facets/interval.rb', line 67 def exclude_first? ; @exclude_first ; end |
#exclude_last? ⇒ Boolean Also known as: exclude_end?
68 |
# File 'lib/standard/facets/interval.rb', line 68 def exclude_last? ; @exclude_last ; end |
#first ⇒ Object Also known as: begin
Returns the first or last sentinal of the interval.
63 |
# File 'lib/standard/facets/interval.rb', line 63 def first ; @first ; end |
#first_closed ⇒ Object
Returns a new interval with one of the two sentinels opened or closed
104 |
# File 'lib/standard/facets/interval.rb', line 104 def first_closed ; Interval.new(@first, @last, false, true) ; end |
#first_opened ⇒ Object
106 |
# File 'lib/standard/facets/interval.rb', line 106 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.
99 100 101 |
# File 'lib/standard/facets/interval.rb', line 99 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.
150 151 152 153 154 155 |
# File 'lib/standard/facets/interval.rb', line 150 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
64 |
# File 'lib/standard/facets/interval.rb', line 64 def last ; @last ; end |
#last_closed ⇒ Object
105 |
# File 'lib/standard/facets/interval.rb', line 105 def last_closed ; Interval.new(@first, @last, true, false) ; end |
#last_opened ⇒ Object
107 |
# File 'lib/standard/facets/interval.rb', line 107 def last_opened ; Interval.new(@first, @last, false, true) ; end |
#max ⇒ Object
Returns the greater of the first and last sentinals.
145 146 147 |
# File 'lib/standard/facets/interval.rb', line 145 def max ((@first <=> @last) == 1) ? @first : @last end |
#min ⇒ Object
Returns the lesser of the first and last sentinals.
140 141 142 |
# File 'lib/standard/facets/interval.rb', line 140 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
.
80 |
# File 'lib/standard/facets/interval.rb', line 80 def null? ; @direction == 0 and @exclusive_first and @exclusive_last ; end |
#opened ⇒ Object
Returns a new interval exclusive of both sentinels.
94 |
# File 'lib/standard/facets/interval.rb', line 94 def opened; Interval.new(@first, @last, true, true) ; end |
#reversed ⇒ Object
Returns a new interval with the sentinels reversed.
(0..10).reversed #=> 10..0
119 120 121 |
# File 'lib/standard/facets/interval.rb', line 119 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]
58 59 60 |
# File 'lib/standard/facets/interval.rb', line 58 def sentinels return [@first, @last] end |
#step(n = 1, d = nil) ⇒ Object
:yield:
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
# File 'lib/standard/facets/interval.rb', line 230 def step(n=1, d=nil) # :yield: return (n < 0 ? @last : @first) if degenerate? # is this right for all values of n ? if block_given? 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 else Enumerator.new(self, :step, n, d) end end |