Class: Range

Inherits:
Object show all
Defined in:
lib/rmtools/enumerable/range.rb,
lib/rmtools/rand/range.rb,
lib/rmtools/conversions/ip.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



232
233
234
# File 'lib/rmtools/enumerable/range.rb', line 232

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

#<=>(range) ⇒ Object



178
179
180
# File 'lib/rmtools/enumerable/range.rb', line 178

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

#>>(i) ⇒ Object

Move range as interval right



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

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

#^(range) ⇒ Object

Diff



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

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



221
222
223
# File 'lib/rmtools/enumerable/range.rb', line 221

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



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

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
138
139
# 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"
    # activerecord 4.0 tells it must not raise
    false
  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



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

def max(&fun)
  return last if !fun or 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



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

def min(&fun)
  return first if !fun or 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



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

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



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

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)


151
152
153
154
155
# File 'lib/rmtools/enumerable/range.rb', line 151

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



166
167
168
169
170
# File 'lib/rmtools/enumerable/range.rb', line 166

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