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]
Methods
Included Modules
Public Class methods
[]( *args )
# File lib/more/facets/interval.rb, line 89
  def self.[]( *args )
    self.new( *args )
  end
new(first, last, exclude_first=false, exclude_last=false )
# 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
Public Instance methods
+@()

Unary shorthands. These return a new interval exclusive of first, last or both sentinels, repectively.

# File lib/more/facets/interval.rb, line 159
  def +@ ; Interval.new(first, last, true, false) ; end
-@()
# File lib/more/facets/interval.rb, line 160
  def -@ ; Interval.new(first, last, false, true) ; end
===(x)

Alias for include?

begin()

Alias for first

closed()

Returns a new interval inclusive of of both sentinels.

# File lib/more/facets/interval.rb, line 139
  def closed; Interval.new(@first, @last, true, true) ; end
degenerate?()

Returns true if the start and end sentinels are equal and the interval is closed; otherwise false.

# File lib/more/facets/interval.rb, line 125
  def degenerate? ; @direction == 0 and ! (@exclusive_first or @exclusive_last) ;  end
direction()

Returns the direction of the interval indicated by +1, 0 or -1.

  (1..5).direction  #=> 1
  (5..1).direction  #=> -1
  (1..1).direction  #=> 0
# File lib/more/facets/interval.rb, line 136
  def direction ; @direction ; end
distance()

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.

This method is also aliased as length size
# 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
each(n=1, d=nil) {|| ...}

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
This method is also aliased as step
# 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
end()

Alias for last

eql?(other)

Compares two intervals to see if they are equal

# 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
exclude_begin?()

Alias for exclude_first?

exclude_end?()

Alias for exclude_last?

exclude_first?()
This method is also aliased as exclude_begin?
# File lib/more/facets/interval.rb, line 115
  def exclude_first? ; @exclude_first ; end
exclude_last?()
This method is also aliased as exclude_end?
# File lib/more/facets/interval.rb, line 116
  def exclude_last? ; @exclude_last ; end
first()

Returns the first or last sentinal of the interval.

This method is also aliased as begin
# File lib/more/facets/interval.rb, line 111
  def first ; @first ; end
first_closed()

Returns a new interval with one of the two sentinels opened or closed

# File lib/more/facets/interval.rb, line 152
  def first_closed ; Interval.new(@first, @last, false, true) ; end
first_opened()
# File lib/more/facets/interval.rb, line 154
  def first_opened ; Interval.new(@first, @last, true, false) ; end
half_closed(e=false)

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.

# 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
include?(x)

Returns true or false if the element is part of the interval.

This method is also aliased as === member?
# 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
last()
This method is also aliased as end
# File lib/more/facets/interval.rb, line 112
  def last ; @last ; end
last_closed()
# File lib/more/facets/interval.rb, line 153
  def last_closed ; Interval.new(@first, @last, true, false) ; end
last_opened()
# File lib/more/facets/interval.rb, line 155
  def last_opened ; Interval.new(@first, @last, false, true) ; end
length()

Alias for distance

max()

Returns the greater of the first and last sentinals.

# File lib/more/facets/interval.rb, line 192
  def max
    ((@first <=> @last) == 1) ? @first : @last
  end
member?(x)

Alias for include?

min()

Returns the lesser of the first and last sentinals.

# File lib/more/facets/interval.rb, line 187
  def min
    ((@first <=> @last) == -1) ? @first : @last
  end
null?()

Returns true if the start and end sentinels are equal and the interval is open; otherwise false.

# File lib/more/facets/interval.rb, line 128
  def null? ; @direction == 0 and @exclusive_first and @exclusive_last ; end
opened()

Returns a new interval exclusive of both sentinels.

# File lib/more/facets/interval.rb, line 142
  def opened; Interval.new(@first, @last, true, true) ; end
reversed()

Returns a new interval with the sentinels reversed.

  (0..10).reversed  #=> 10..0
# File lib/more/facets/interval.rb, line 167
  def reversed
    Interval.new(@last, @first, true, true)
  end
sentinels()

Returns a two element array of first and last sentinels.

 (0..10).sentinels   #=> [0,10]
# File lib/more/facets/interval.rb, line 106
  def sentinels
    return [@first, @last]
  end
size()

Alias for distance

step(n=1, d=nil)

Alias for each

~()
# File lib/more/facets/interval.rb, line 161
  def ~@ ; Interval.new(first, last, true, true) ; end