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



228
229
230
# File 'lib/rmtools/enumerable/range.rb', line 228

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

#<=>(range) ⇒ Object



174
175
176
# File 'lib/rmtools/enumerable/range.rb', line 174

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

#>>(i) ⇒ Object

Move range as interval right



223
224
225
# File 'lib/rmtools/enumerable/range.rb', line 223

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

#^(range) ⇒ Object

Diff



169
170
171
172
# File 'lib/rmtools/enumerable/range.rb', line 169

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



217
218
219
# File 'lib/rmtools/enumerable/range.rb', line 217

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



212
213
214
# File 'lib/rmtools/enumerable/range.rb', line 212

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
# File 'lib/rmtools/enumerable/range.rb', line 127

def include?(number_or_range)
  if Numeric === number_or_range
    include_number? number_or_range
  elsif XRange === number_or_range
    number_or_range.include? self
  else
    include_number? number_or_range.first and include_number? number_or_range.last
  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



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

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



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

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



208
209
210
# File 'lib/rmtools/enumerable/range.rb', line 208

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



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

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)


147
148
149
150
151
# File 'lib/rmtools/enumerable/range.rb', line 147

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



162
163
164
165
166
# File 'lib/rmtools/enumerable/range.rb', line 162

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