rmtools 2.0.0.rc5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -44,9 +44,9 @@ It's still randomly documented since it's just my working tool.
44
44
  * Added private #alias_constant
45
45
 
46
46
  * Array
47
- * #avg and #avg_by for an empty array now return 0
48
- * Fixed #rand_by for an empty array
49
- * Added #intersects? aliased as #x?
47
+ * Fixed #avg, #avg_by, #rand_by in case of an empty array: return nil
48
+ * Added #intersects? (alias #x?)
49
+ * Added #rfind_by, #runiq_by and few more iterators
50
50
 
51
51
  * Symbol
52
52
  * Added #+, #split and #method_missing to proxy all possible methods to #to_s
@@ -69,12 +69,12 @@ class Array
69
69
 
70
70
  # arithmetics
71
71
  def avg
72
- empty? ? 0 : sum.to_f/size
72
+ empty? ? nil : sum.to_f/size
73
73
  end
74
74
 
75
75
  # for use with iterators
76
- def avg_by(&b)
77
- empty? ? 0 : sum(&b).to_f/size
76
+ def avg_by(&block)
77
+ empty? ? nil : sum(&block).to_f/size
78
78
  end
79
79
 
80
80
  def scale(top)
@@ -140,7 +140,7 @@ class Array
140
140
  end
141
141
 
142
142
 
143
- # selectors
143
+ # filters
144
144
  def odds
145
145
  values_at(*(0...size).odds)
146
146
  end
@@ -150,10 +150,6 @@ class Array
150
150
  end
151
151
 
152
152
  # conditional
153
- def uniq?
154
- uniq == self
155
- end
156
-
157
153
  def every?
158
154
  !find {|e| !yield(e)}
159
155
  end
@@ -162,10 +158,24 @@ class Array
162
158
  !find {|e| yield(e)}
163
159
  end
164
160
 
161
+ # rightmost #find
162
+ def rfind
163
+ reverse_each {|e| return e if yield e}
164
+ nil
165
+ end
166
+
167
+ # find-by-value filters
168
+ # It's more of a pattern. Use of respective meta-iterators is prefered.
165
169
  def find_by(key, value)
166
170
  find {|e| e.__send__(key) == value}
167
171
  end
168
172
 
173
+ # rightmost #find_by
174
+ def rfind_by(key, value)
175
+ reverse_each {|e| return e if e.__send__(key) == value}
176
+ nil
177
+ end
178
+
169
179
  def select_by(key, value)
170
180
  select {|e| e.__send__(key) == value}
171
181
  end
@@ -174,11 +184,28 @@ class Array
174
184
  reject {|e| e.__send__(key) == value}
175
185
  end
176
186
 
187
+
188
+ # uniqs
189
+ def uniq?
190
+ uniq == self
191
+ end
192
+
193
+ # rightmost #uniq
194
+ def runiq
195
+ reverse.uniq.reverse
196
+ end
197
+
198
+ # rightmost #uniq_by
199
+ def runiq_by(&block)
200
+ reverse.uniq_by(&block).reverse
201
+ end
202
+
177
203
  # sort / group
178
204
  def sorted_uniq_by(&block)
179
205
  uniq_by(&block).sort_by(&block)
180
206
  end
181
207
 
208
+ # C-function, see it in /ext
182
209
  def arrange_by(*args, &block)
183
210
  arrange(*args, &block)
184
211
  end
@@ -243,23 +270,13 @@ class Array
243
270
 
244
271
 
245
272
  # mapreduce
246
- def sum(identity=0, &b) foldl(:+, &b) || identity end
247
-
248
- # fastering activesupport's method
249
- def group_by(&b) arrange(:group, &b) end
250
-
251
-
252
-
253
-
254
- # rightmost #find
255
- def rfind
256
- reverse_each {|e| return e if yield e}
257
- nil
273
+ def sum(identity=0, &b)
274
+ foldl(:+, &b) || identity
258
275
  end
259
276
 
260
- # rightmost #uniq
261
- def runiq
262
- reverse.uniq.reverse
277
+ # fastering activesupport's method
278
+ def group_by(&b)
279
+ arrange(:group, &b)
263
280
  end
264
281
 
265
282
  end
@@ -236,7 +236,7 @@ class Array
236
236
  err.message << " (`#{method}' interpreted as decorator-function `#{meth}')"
237
237
  raise err
238
238
  end}
239
- when :find_by, :select_by, :reject_by
239
+ when :find_by, :rfind_by,:select_by, :reject_by
240
240
  Array.class_eval %{
241
241
  def #{method}(val)
242
242
  # select_by_count(max_count) =>
@@ -268,11 +268,11 @@ class Array
268
268
  if Array === value
269
269
  # owner_ids = users_ids =>
270
270
  # each_with_index {|e, i| e.owner_id = users_ids[i]}
271
- each_with_index {|e, i| e.__send__ meth, value[i]}
271
+ each_with_index {|e, i| e.#{meth} value[i]}
272
272
  else
273
273
  # owner_ids = user_id =>
274
274
  # each {|e, i| e.owner_id = user_id}
275
- each {|e| e.__send__ meth, value}
275
+ each {|e| e.#{meth} value}
276
276
  end
277
277
  rescue NoMethodError => err
278
278
  err.message << " (`#{method}' interpreted as map-function `#{meth}')"
@@ -17,53 +17,91 @@ end
17
17
  # # => Interval[[-Inf, -2], [-1, +Inf]]
18
18
  # 2) Hardly we can use Range second way, when it defined by non-integers:
19
19
  # [0,1,2,3,4,5][1.9..4] # => [1, 2, 3, 4]
20
- # (1.9...4.1).include? 4 # => true, BUT
20
+ # (1.9...4.1).include? 4 # => true, AND
21
+ # (1.9...4.1).include? 1 # => false, BUT
21
22
  # [0,1,2,3,4,5][1.9...4.1] # => [1, 2, 3]
22
23
  #
23
- # This extension leaves the first problem for a purpose of solving the second, saving the capability of a Range syntactic sugar. The present extension applies Set operations to ranges considered as lists of contained integers.
24
- # It means:
25
- # (x..y) equivalent (x...y+0.5) equivalent (x...y+1) equivalent [x, x+1, ..., y]
26
- # Note quantity of dots (end exclusion)
24
+ # A domain of the present extension is Set operations with ranges considered as lazy lists of integers.
25
+ # The present extension is solving the second problem, yet
26
+ # * saving the capability of a Range syntactic sugar;
27
+ # * does exactly extend and not modify the Range behaviour.
28
+ # These methods support only numeric ranges, it won't work with chars and datetime borders,
29
+ # though I'll make a support for the Date and Time in a future version.
27
30
  class Range
28
31
 
29
- def included_end
30
- exclude_end? ? last.integer? ? last - 1 : last.to_i : last
32
+ private
33
+ def next_int(n)
34
+ n.integer? || n.to_i == n ? n.to_i + 1 : n.ceil
35
+ end
36
+
37
+ def prev_int(n)
38
+ n.integer? || n.to_i == n ? n.to_i - 1 : n.to_i
39
+ end
40
+
41
+ def int_end
42
+ exclude_end? ? prev_int(last) : last.to_i
31
43
  end
44
+ public
32
45
 
46
+ # End inclusion need to universalize ranges for use in XRange list.
47
+ # 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
48
+ # #include_end should not be coupled with #size and #empty? which have their own "..." handling
49
+ # (0.9...1.3).include_end # => 0.9..1, BUT
50
+ # (0.3...0.5).include_end # => 0.3..0
33
51
  def include_end
34
- exclude_end? ? first..included_end : self
52
+ exclude_end? ? first..prev_int(last) : self
35
53
  end
36
54
 
37
- # Since it's not represent an interval...
38
- # (0..0).size # => 1 (equivalent list of one zero)
39
- # (0...0).size # => 0 (equivalent empty list)
40
- # There is no empty ranges with end included (since it includes at least an end, right?)
55
+ def included_end
56
+ exclude_end? ? prev_int(last) : last
57
+ end
58
+
59
+ # Represent a count of integers that range include and not real interval length
60
+ # (0..0).size
61
+ # => 1 (equivalent list of one 0)
62
+ # (0...0).size
63
+ # => 0 (equivalent empty list)
64
+ # (0.3..0.5).size
65
+ # => 0 (there is no integer between 0.3 and 0.5)
66
+ # (0.9...1.1).size
67
+ # => 1 (equivalent list of one 1 which is between 0.9 and 1.1)
68
+ # (2..1).size
69
+ # => 0 (such a range just does't make sense)
41
70
  def size
42
- exclude_end? ? (last - first).abs : (last - first).abs+1
71
+ [int_end - first.ceil + 1, 0].max
43
72
  end
44
73
 
74
+ # Include any integer?
45
75
  def empty?
46
- exclude_end? && last == first
76
+ size == 0
47
77
  end
48
78
 
49
79
  def b
50
- !empty? && self
80
+ size != 0 && self
81
+ end
82
+
83
+ # Simplify a range to in-domain equivalent with integer edges.
84
+ def integerize
85
+ first.ceil..int_end
51
86
  end
52
87
 
53
- # Let significant content of a range used this way be:
88
+ # Significant content of a range then.
54
89
  def integers
55
- (first.ceil..included_end).to_a
90
+ integerize.to_a
56
91
  end
57
92
  alias :to_is :integers
58
93
 
59
- # -(1..2)
94
+ # Unfortunately, Range's start point can not be excluded, thus there is no *true inversion* of a range with included end.
95
+ # Though, is this domain we can "integerize" range, then
96
+ # -(1..2)
60
97
  # -(0.5..2.1)
61
- # i.e. all excluding these lazy indices: [1, 2]
62
- ### => XRange(-∞..0, 3..∞)
98
+ # (i.e. all excluding these indices: [1, 2])
99
+ ### => XRange(-∞..0, 3..+∞)
63
100
  def -@
64
- XRange(-Inf..first.ceil-1, (exclude_end? && last.integer? ? last : last.to_i+1)..Inf)
101
+ XRange(-Inf..prev_int(first), (exclude_end? ? last.ceil : next_int(last))..Inf)
65
102
  end
66
103
 
104
+ # Intersection
67
105
  def &(range)
68
106
  return range & self if range.is XRange
69
107
  fst = [first, range.first].max
@@ -72,17 +110,20 @@ class Range
72
110
  end
73
111
 
74
112
  # On the basis of #-@ for non-integers,
75
- # (0..3) - (0.5..2.1)
76
113
  # (0..3) - (1..2)
77
- ### => XRange(0..0, 3..3.0)
114
+ # (0..3) - (0.5..2.1)
115
+ ### => XRange(0..0, 3..3)
78
116
  def -(range)
79
117
  self & -range
80
118
  end
81
119
 
82
120
  alias :include_number? :include?
83
- # This function corresponds with ruby's default one in that we consider any number as a point on a segment.
84
- # Thus, any of these 0..1, 0..1.0
85
- # would return true as for 1 so as for 1.0
121
+ # #include? corresponds with Ruby's default one, which considers a range as an interval
122
+ # (0..1).include? 1.0
123
+ # 1.in 0..1
124
+ # => true
125
+ # and (0...1.0).include? 1.0
126
+ # => false
86
127
  def include?(number_or_range)
87
128
  if Numeric === number_or_range
88
129
  include_number? number_or_range
@@ -93,36 +134,38 @@ class Range
93
134
  end
94
135
  end
95
136
 
96
- def x?(range)
97
- return false if empty?
137
+ # Does these ranges have at least one common point?
138
+ # (0..1).x? 1..2
139
+ # (1...2).x? 0..1
140
+ # (0..3).x? 1..2
141
+ # (1..2).x? 0..3
142
+ # => true
143
+ # (0..1.4).x? 1.5..2
144
+ # (0...1).x? 1..2
145
+ # (2..3).x? 0..1
146
+ # => false
147
+ def x?(range, pretend_not_exclude=false)
98
148
  return range.x? self if range.is XRange
99
- range_end = range.included_end
100
- if range_end < range.first
101
- return x?(range_end..range.first)
102
- end
103
- self_end = included_end
104
- if self_end < first
105
- return (first..self_end).x?(range)
106
- end
107
- case self_end <=> range_end
108
- when -1
109
- self_end >= range.first
110
- when 1
111
- first <= range_end >= first
112
- else
113
- true
114
- end
149
+ (range.last > first or ((!range.exclude_end? or pretend_not_exclude) and range.last == first)) and
150
+ (range.first < last or ((!exclude_end? or pretend_not_exclude) and range.first == last))
115
151
  end
116
152
  alias :intersects? :x?
117
153
 
154
+ # Union
155
+ # (1..3) | (2..4)
156
+ # => 1..4
157
+ # (1...2) | (2..4)
158
+ # => 1..4
159
+ # (1..2) | (3..4)
160
+ # => XRange(1..2, 3..4)
161
+ # A result will be inadequate if any range is not integered and excludes end
118
162
  def |(range)
119
163
  return range | self if range.is XRange
120
- range = range.include_end
121
- self_ = self.include_end
122
- return XRange.new self, range if !x?(range)
123
- [self.begin, range.begin].min..[self_.end, range.end].max
164
+ return XRange.new self, range if !x?(range, true)
165
+ [first, range.first].min..[included_end, range.included_end].max
124
166
  end
125
167
 
168
+ # Diff
126
169
  def ^(range)
127
170
  common = self & range
128
171
  self - common | range - common
@@ -132,53 +175,12 @@ class Range
132
175
  (first <=> range.first).b || included_end <=> range.included_end
133
176
  end
134
177
 
135
- def center
136
- (first + include_end.last)/2
137
- end
138
-
139
- def part(i, j)
140
- first + (i-1)*size/j .. first - 1 + i*size/j unless i < 1 or j < 1 or j < i
141
- end
142
-
143
- def div(n)
144
- unless n < 1
145
- rarray = []
146
- j = self.begin
147
- iend = include_end.end
148
- rarray << (j..(j+=n)-1) until j+n > iend
149
- rarray << (j..iend)
150
- end
151
- end
152
-
153
- def /(i)
154
- part 1, i
155
- end
156
-
157
- def >>(i)
158
- self.begin + i .. include_end.end + i
159
- end
160
-
161
- def <<(i)
162
- self.begin - i .. include_end.end - i
163
- end
164
-
165
- def of(ary)
166
- ary[self]
167
- end
168
-
169
- def odds
170
- select {|i| i%2 != 0}
171
- end
172
-
173
- def evens
174
- select {|i| i%2 == 0}
175
- end
176
-
178
+ # Sum of integers in a range
177
179
  def sum
178
- ie = include_end.end
179
- return (1..ie).sum - (0..-self.begin).sum if self.begin < 0
180
- return 0 if ie < self.begin
181
- ie*(ie+1)/2 - (1..self.begin-1).sum
180
+ last = included_end
181
+ return (1..last).sum - (0..-first).sum if first < 0
182
+ return 0 if last <= first
183
+ last*(last+1)/2 - (1..first-1).sum
182
184
  end
183
185
 
184
186
  # minimum of monotone function definition interval
@@ -203,6 +205,30 @@ class Range
203
205
  end
204
206
  end
205
207
 
208
+ def odds
209
+ select {|i| i%2 != 0}
210
+ end
211
+
212
+ def evens
213
+ select {|i| i%2 == 0}
214
+ end
215
+
216
+ # Average
217
+ def center
218
+ (first + included_end)/2
219
+ end
220
+ alias :avg :center
221
+
222
+ # Move range as interval right
223
+ def >>(i)
224
+ first + i .. included_end + i
225
+ end
226
+
227
+ # Move range as interval left
228
+ def <<(i)
229
+ first - i .. included_end - i
230
+ end
231
+
206
232
  end
207
233
 
208
234
  class XRange
@@ -337,7 +363,7 @@ public
337
363
  end
338
364
 
339
365
  def inspect
340
- "XRange(#{@ranges.join(', ').gsub('Infinity', '')})"
366
+ "XRange(#{@ranges.join(', ').gsub('-Infinity', '-∞').gsub('Infinity', '+∞')})"
341
367
  end
342
368
 
343
369
  end
@@ -1,3 +1,3 @@
1
1
  module RMTools
2
- VERSION = '2.0.0.rc5'
2
+ VERSION = '2.0.0'
3
3
  end
@@ -19,11 +19,11 @@ module LibXML
19
19
  x.gsub! %r{(^|/)([^.#\w*/'"])}, '\1*\2'
20
20
  x.gsub! %r{\[([a-z]+)}, '[@\1'
21
21
  x.gsub! %r{(\[(?:@\w+!?=)?)['"]?([^'"\]@]+)['"]?\]}, '\1"\2"]'
22
- if x !~%r{^(#{
23
- }(\./|\.//|/|//)?#{
24
- }(\w+:)?(\w+|\*)#{ # [ns:]name
25
- }(\[(@\w+(!?="[^"]+")?|"[^"]+")\])*#{# attributes [@href!="pics/xynta.jpeg"]
26
- }(\[-?\d+\]|\{[^\}]+\})?#{ # inruby-filters (see finder functions ^)
22
+ if x !~%r{^(?:#{
23
+ }(?:\./|\.//|/|//)?#{
24
+ }(?:(?:[\w\-]+:)?[\w\-]+|\*)#{ # [ns:]name
25
+ }(?:\[(?:@\w+(!?="[^"]+")?|"[^"]+")\])*#{# attributes [@href!="pics/xynta.jpeg"]
26
+ }(?:\[-?\d+\]|\{[^\}]+\})?#{ # inruby-filters (see finder functions ^)
27
27
  })+$}
28
28
  raise XML::Error, "can't process `#{xpath}`; #{x}"
29
29
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rmtools
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.rc5
5
- prerelease: 6
4
+ version: 2.0.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Sergey Baev
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-21 00:00:00.000000000 Z
12
+ date: 2013-05-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -182,9 +182,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
182
182
  required_rubygems_version: !ruby/object:Gem::Requirement
183
183
  none: false
184
184
  requirements:
185
- - - ! '>'
185
+ - - ! '>='
186
186
  - !ruby/object:Gem::Version
187
- version: 1.3.1
187
+ version: '0'
188
188
  requirements: []
189
189
  rubyforge_project:
190
190
  rubygems_version: 1.8.24