Class: Range

Inherits:
Object show all
Defined in:
lib/rmtools/enumerable/range.rb,
lib/rmtools/rand/range.rb,
lib/rmtools/conversions/ip.rb,
lib/rmtools/conversions/numeric.rb

Overview

Range in Ruby can have at least 2 meanings: 1) interval of real numbers (0…2).include? 1.6 # => true 2) lazy list of array indices (included integers):

0,1,2,3,4,5][1..4

# => [1, 2, 3, 4]

There is some basic problems. 1) For the first way of using, Range doesn’t have Set operations. Further more Range can not be complex. There is “intervals” gem that partially solves these problems, but it’s arithmetic is not Set compatible: -Interval # => Interval[-2, -1] instead of # => Interval[[-Inf, -2], [-1, +Inf]] 2) Hardly we can use Range second way, when it defined by non-integers:

0,1,2,3,4,5][1.9..4

# => [1, 2, 3, 4]

(1.9…4.1).include? 4 # => true, AND (1.9…4.1).include? 1 # => false, BUT

0,1,2,3,4,5][1.9…4.1

# => [1, 2, 3]

A domain of the present extension is Set operations with ranges considered as lazy lists of integers. The present extension is solving the second problem, yet

  • saving the capability of a Range syntactic sugar;

  • does exactly extend and not modify the Range behaviour.

These methods support only numeric ranges, it won’t work with chars and datetime borders, though I’ll make a support for the Date and Time in a future version.

Instance Method Summary collapse

Instance Method Details

#&(range) ⇒ Object

Intersection



105
106
107
108
109
110
# File 'lib/rmtools/enumerable/range.rb', line 105

def &(range)
  return range & self if range.is XRange
  fst = [first, range.first].max
  lst = [included_end, range.included_end].min
  fst > lst ? nil : fst..lst
end

#-(range) ⇒ Object

On the basis of #-@ for non-integers, (0..3) - (1..2) (0..3) - (0.5..2.1)

> XRange(0..0, 3..3)



116
117
118
# File 'lib/rmtools/enumerable/range.rb', line 116

def -(range)
  self & -range
end

#-@Object

Unfortunately, Range’s start point can not be excluded, thus there is no *true inversion* of a range with included end. Though, is this domain we can “integerize” range, then -(1..2) -(0.5..2.1) (i.e. all excluding these indices: [1, 2])

> XRange(-∞..0, 3..+∞)



100
101
102
# File 'lib/rmtools/enumerable/range.rb', line 100

def -@
  XRange(-Inf..prev_int(first), (exclude_end? ? last.ceil : next_int(last))..Inf)
end

#<<(i) ⇒ Object

Move range as interval left



230
231
232
# File 'lib/rmtools/enumerable/range.rb', line 230

def <<(i)
  first - i .. included_end - i
end

#<=>(range) ⇒ Object



176
177
178
# File 'lib/rmtools/enumerable/range.rb', line 176

def <=>(range) 
  (first <=> range.first).b || included_end <=> range.included_end
end

#>>(i) ⇒ Object

Move range as interval right



225
226
227
# File 'lib/rmtools/enumerable/range.rb', line 225

def >>(i)
  first + i .. included_end + i
end

#^(range) ⇒ Object

Diff



171
172
173
174
# File 'lib/rmtools/enumerable/range.rb', line 171

def ^(range)
  common = self & range
  self - common | range - common
end

#bObject



79
80
81
# File 'lib/rmtools/enumerable/range.rb', line 79

def b
  size != 0 && self
end

#centerObject Also known as: avg

Average



219
220
221
# File 'lib/rmtools/enumerable/range.rb', line 219

def center
  (first + included_end)/2 
end

#empty?Boolean

Include any integer?

Returns:

  • (Boolean)


75
76
77
# File 'lib/rmtools/enumerable/range.rb', line 75

def empty?
  size == 0
end

#evensObject



214
215
216
# File 'lib/rmtools/enumerable/range.rb', line 214

def evens
  select {|i| i%2 == 0}
end

#include?(number_or_range) ⇒ Boolean

#include? corresponds with Ruby’s default one, which considers a range as an interval (0..1).include? 1.0 1.in 0..1

> true

and (0…1.0).include? 1.0

> false

Returns:

  • (Boolean)


127
128
129
130
131
132
133
134
135
136
137
# File 'lib/rmtools/enumerable/range.rb', line 127

def include?(number_or_range)
  if Numeric === number_or_range or String === number_or_range
    include_number? number_or_range
  elsif XRange === number_or_range
    number_or_range.include? self
  elsif Range === number_or_range
    include_number? number_or_range.first and include_number? number_or_range.last
  else
    raise TypeError, "can not find #{number_or_range.class} in Range"
  end
end

#include_endObject

End inclusion need to universalize ranges for use in XRange list. Considering the extension domain, one simply should not use “…” notation, but if such a range nevertheless appears as an argument, we reduce that to an operable one at the cost of a fractional accuracy #include_end should not be coupled with #size and #empty? which have their own “…” handling (0.9…1.3).include_end # => 0.9..1, BUT (0.3…0.5).include_end # => 0.3..0



51
52
53
# File 'lib/rmtools/enumerable/range.rb', line 51

def include_end
  exclude_end? ? first..prev_int(last) : self
end

#include_number?Object



120
# File 'lib/rmtools/enumerable/range.rb', line 120

alias :include_number? :include?

#included_endObject



55
56
57
# File 'lib/rmtools/enumerable/range.rb', line 55

def included_end
  exclude_end? ? prev_int(last) : last
end

#integerizeObject

Simplify a range to in-domain equivalent with integer edges.



84
85
86
# File 'lib/rmtools/enumerable/range.rb', line 84

def integerize
  first.ceil..int_end
end

#integersObject Also known as: to_is

Significant content of a range then.



89
90
91
# File 'lib/rmtools/enumerable/range.rb', line 89

def integers
  integerize.to_a
end

#mask_ipObject



72
73
74
75
76
77
78
79
80
# File 'lib/rmtools/conversions/ip.rb', line 72

def mask_ip
  i = nil
  31.downto(12) {|i|
    lm = last.mask_ip(i)
    break if first.mask_ip(i) == lm and (last+1).mask_ip(i) != lm
    i = nil
  }
  i || 32
end

#max(&fun) ⇒ Object

maximum of monotone function definition interval



200
201
202
203
204
205
206
207
208
# File 'lib/rmtools/enumerable/range.rb', line 200

def max(&fun)
  return last if yield last
  return unless yield first
  if yield(c = center)
    (c+1..last-1).max(&fun) || c
  else 
    (first..c-1).max(&fun)
  end
end

#min(&fun) ⇒ Object

minimum of monotone function definition interval



189
190
191
192
193
194
195
196
197
# File 'lib/rmtools/enumerable/range.rb', line 189

def min(&fun)
  return first if yield first
  return unless yield last
  if yield(c = center)
    (first+1..c-1).min(&fun) || c
  else
    (c+1..last).min(&fun)
  end
end

#oddsObject



210
211
212
# File 'lib/rmtools/enumerable/range.rb', line 210

def odds
  select {|i| i%2 != 0}
end

#randObject



6
7
8
# File 'lib/rmtools/rand/range.rb', line 6

def rand
  self.begin + Kernel.rand(size)
end

#randsegObject



10
11
12
# File 'lib/rmtools/rand/range.rb', line 10

def randseg
  (a = rand) > (b = rand) ? b..a : a..b
end

#sizeObject

Represent a count of integers that range include and not real interval length (0..0).size

> 1 (equivalent list of one 0)

(0…0).size

> 0 (equivalent empty list)

(0.3..0.5).size

> 0 (there is no integer between 0.3 and 0.5)

(0.9…1.1).size

> 1 (equivalent list of one 1 which is between 0.9 and 1.1)

(2..1).size

> 0 (such a range just does’t make sense)



70
71
72
# File 'lib/rmtools/enumerable/range.rb', line 70

def size
  [int_end - first.ceil + 1, 0].max
end

#sumObject

Sum of integers in a range



181
182
183
184
185
186
# File 'lib/rmtools/enumerable/range.rb', line 181

def sum
  last = included_end
  return (1..last).sum - (0..-first).sum if first < 0
  return 0 if last <= first
  last*(last+1)/2 - (1..first-1).sum
end

#x?(range, pretend_not_exclude = false) ⇒ Boolean Also known as: intersects?

Does these ranges have at least one common point? (0..1).x? 1..2 (1…2).x? 0..1 (0..3).x? 1..2 (1..2).x? 0..3

> true

(0..1.4).x? 1.5..2 (0…1).x? 1..2 (2..3).x? 0..1

> false

Returns:

  • (Boolean)


149
150
151
152
153
# File 'lib/rmtools/enumerable/range.rb', line 149

def x?(range, pretend_not_exclude=false)
  return range.x? self if range.is XRange
  (range.last > first or ((!range.exclude_end? or pretend_not_exclude) and range.last == first)) and
  (range.first < last or ((!exclude_end? or pretend_not_exclude) and range.first == last))
end

#|(range) ⇒ Object

Union (1..3) | (2..4)

> 1..4

(1…2) | (2..4)

> 1..4

(1..2) | (3..4)

> XRange(1..2, 3..4)

A result will be inadequate if any range is not integered and excludes end



164
165
166
167
168
# File 'lib/rmtools/enumerable/range.rb', line 164

def |(range)
  return range | self if range.is XRange
  return XRange.new self, range if !x?(range, true)
  [first, range.first].min..[included_end, range.included_end].max
end