iterable 0.0.6.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ require_relative './iterable_array.rb'
2
+
@@ -0,0 +1,94 @@
1
+ # Note: Currently this uses a super sloppy/cluttered approach to adding and removing methods.
2
+ # I'd put the methods in modules and extend the IterableArray instance with the appropriate
3
+ # module except that removing/unextending a module would require the mixology gem, which currently
4
+ # does not work in Rubinius. :(
5
+ # Or the remix gem, maybe.
6
+ require 'forwardable'
7
+
8
+ require_relative './swapable.rb'
9
+ require_relative './iterable_array/special_accessors.rb'
10
+ require_relative './iterable_array/iterators.rb'
11
+ require_relative './iterable_array/iterator_specials.rb'
12
+ require_relative './iterable_array/special_modifiers_noniterating.rb'
13
+ # The following are not yet modules.
14
+ require_relative './iterable_array/special_modifiers_iterating.rb'
15
+ require_relative './iterable_array/plain_modifiers.rb'
16
+
17
+ class IterableArray
18
+ extend Forwardable
19
+
20
+ # include self::SpecialAccessors
21
+ # include self::Iterators
22
+ include self::IteratorSpecials
23
+ include self::SpecialModifiersNoniterating
24
+ # include self::SpecialModifiersIterating
25
+ # include self::PlainModifiers
26
+
27
+ # Most Enumerable instance methods have not been tested to ensure atomic modification
28
+ include Enumerable
29
+
30
+ @@iterator_specials = [ :tracking, :tracking=, :invert_tracking, ]
31
+
32
+ @@plain_accessors = [ :frozen?, :==, :[]=, :size, :length, :to_a, :to_s, :to_json, :to_enum, :include?, :hash, :to_ary, :fetch, :inspect, :at, :join, :empty?, :member?, :pack, :shelljoin, ]
33
+ @@special_accessors = [ :<<, :concat, :&, :|, :*, :+, :-, :[], :drop, :dup, :compact, :sample, :slice, :<=>, :eql?, :indices, :indexes, :values_at, :assoc, :rassoc, :first, :sort, :last, :flatten, :reverse, :shuffle, :push, :replace, :rotate, :swap, :swap_indices, :take, :transpose, :uniq, ]
34
+
35
+ @@plain_modifiers = [ :delete, :delete_at, ]
36
+ @@special_modifiers = [ :clear, :compact!, :flatten!, :insert, :move, :move_from, :shift, :shuffle!, :sort!, :sort_by!, :unshift, :pop, :reverse!, :rotate!, :slice!, :swap!, :swap_indices!, :uniq!, ]
37
+
38
+ @@iterators = [ :each, :reverse_each, :rindex, :collect, :collect!, :map, :map!, :combination, :cycle, :delete_if, :drop_while, :each_index, :index, :find_index, :keep_if, :each_with_index, :reject!, :reject, :select!, :select, :take_while, :count, :fill, :permutation, :repeated_permutation, :repeated_combination, :product, :zip, ]
39
+
40
+ # Enumerable methods not covered by Array => [:sort_by, :grep, :find, :detect, :find_all, :flat_map, :collect_concat, :inject, :reduce, :partition, :group_by, :all?, :any?, :one?, :none?, :min, :max, :minmax, :min_by, :max_by, :minmax_by, :each_entry, :each_slice, :each_cons, :each_with_object, :chunk, :slice_before]
41
+
42
+ def_delegators :@array, *@@plain_accessors
43
+ def_delegators :@array, *@@plain_modifiers
44
+
45
+ private
46
+
47
+ def initialize *args
48
+ @array = Array.new(*args).extend Swapable
49
+ @progenitor_binding = binding
50
+ end
51
+
52
+ def bastardize
53
+ @array = self.new_with_binding @array
54
+ @tracking = 0.5
55
+ class << self
56
+ def_delegators :@array, *@@special_accessors
57
+ def_delegators :@array, *@@iterators
58
+ end
59
+ define_plain_modifiers
60
+ define_special_modifiers_iterating
61
+ end
62
+
63
+ def debastardize
64
+ @tracking = nil
65
+ @backward_index, @current_index, @forward_index = nil, nil, nil
66
+ @array = @array.to_a
67
+ remove_methods *@@special_accessors
68
+ remove_methods *@@iterators
69
+ remove_methods *@@plain_modifiers
70
+ remove_methods *@@special_modifiers
71
+ end
72
+
73
+ def remove_methods *ary
74
+ ary.each do |meth|
75
+ singleton_class.class_exec { remove_method meth }
76
+ end
77
+ end
78
+
79
+ protected
80
+
81
+ # Necessary for allowing nested iteration with modifying iterators (like
82
+ # :delete_if)
83
+ def new_with_binding array
84
+ iter_ary = IterableArray.new array
85
+ iter_ary.take_progenitor_binding @progenitor_binding
86
+ iter_ary
87
+ end
88
+
89
+ def take_progenitor_binding progenitor_binding
90
+ @progenitor_binding = progenitor_binding
91
+ class << self; undef_method :take_progenitor_binding; end
92
+ end
93
+ end
94
+
@@ -0,0 +1,68 @@
1
+ class IterableArray
2
+ module IteratorSpecials
3
+ # specials
4
+ # TODO: either ensure that the following methods are being undefined
5
+ # during #debastardize or make them private... currently they aren't
6
+ # being undefined.
7
+ # TODO: Also, these currently fail in a nested iteration block if they're
8
+ # called from the main instance of IterableArray...they'll only affect
9
+ # the outermost iteration block when called by a user (but they should
10
+ # work fine when called internally)
11
+ public
12
+ # untested
13
+ def invert_tracking
14
+ @tracking *= -1
15
+ tracking
16
+ end
17
+
18
+ # Need to add some minimal documentation on tracking.
19
+ # untested
20
+ def invert_tracking_at nesting_level = -1
21
+ direct_to nesting_level, &:invert_tracking
22
+ end
23
+
24
+ # untested
25
+ def tracking
26
+ case @tracking < 0
27
+ when true then :aft
28
+ when false then :fore
29
+ end
30
+ end
31
+
32
+ # untested
33
+ def tracking_at nesting_level = -1
34
+ direct_to nesting_level, &:tracking
35
+ end
36
+
37
+ # untested
38
+ def tracking= orient
39
+ @tracking = case orient
40
+ when :aft then @tracking.abs * -1
41
+ when :fore then @tracking.abs
42
+ end
43
+ tracking
44
+ end
45
+
46
+ # untested
47
+ def tracking_at= orient, nesting_level = -1
48
+ direct_to nesting_level do |obj|
49
+ obj.tracking = orient
50
+ end
51
+ end
52
+
53
+ protected
54
+ # untested
55
+ def direct_to nesting_level
56
+ case
57
+ when nesting_level == 0
58
+ yield self
59
+ when nesting_level < 0
60
+ @array.kind_of? Array ?
61
+ direct_to(0) :
62
+ @array.direct_to(-1)
63
+ else
64
+ @array.direct_to(nesting_level - 1)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,445 @@
1
+ class IterableArray
2
+ # module Iterators
3
+ private
4
+ def catch_a_break
5
+ result = nil
6
+ begin
7
+ bastardize
8
+ result = yield
9
+ ensure
10
+ debastardize
11
+ end
12
+ result
13
+ end
14
+
15
+ def increment_indices
16
+ @current_index = @forward_index
17
+ @forward_index = @current_index + 1
18
+ @backward_index = @current_index - 1
19
+ end
20
+
21
+ public
22
+
23
+ # untested and not a pretty sight in general
24
+ # When used without a block, `#fill` is not iteration aware
25
+ # as its default behavior is similar to that of `#replace`---
26
+ # which is a clobberor.
27
+ def fill *args # obj=nil, start_or_range=nil, lengther=nil
28
+ return @array.fill *args unless block_given?
29
+
30
+ #######################
31
+ # Argument processing #
32
+ #######################
33
+ args[0] = 0 if args.empty?
34
+
35
+ raise ArgumentError if
36
+ args.size > 2 or
37
+ (args.first.kind_of? Range and args.size != 1)
38
+
39
+ if args.first.kind_of? Range then
40
+ args[1] = args.first.exclude_end? ?
41
+ args.first.last - 1 :
42
+ args.first.last
43
+ args[0] = args.first.first
44
+ end
45
+
46
+ ##################################################
47
+ # Translating argument array to iteration bounds #
48
+ ##################################################
49
+ starter = args.first
50
+ the_final = args.at 1
51
+ ender = -> { the_final or length }
52
+
53
+ #############
54
+ # Iteration #
55
+ #############
56
+ catch_a_break do
57
+ @current_index = starter
58
+ @backward_index, @forward_index = @current_index - 1, @current_index + 1
59
+ while @current_index < ender[]
60
+ index_to_fill = @current_index
61
+ self[index_to_fill] = yield @current_index
62
+ increment_indices
63
+ end
64
+ self
65
+ end
66
+ end
67
+
68
+ # untested
69
+ def drop_while
70
+ return @array.to_enum :drop_while unless block_given?
71
+
72
+ out = IterableArray.new
73
+ dropping = true
74
+ each do |x|
75
+ dropping = yield x if dropping
76
+ out << x unless dropping
77
+ end
78
+ out
79
+ end
80
+
81
+ # untested
82
+ def select
83
+ return @array.to_enum(:select) unless block_given?
84
+ out = IterableArray.new
85
+ each { |x| out << x if yield x }
86
+ out
87
+ end
88
+
89
+ # untested
90
+ def select!
91
+ return @array.to_enum(:select) unless block_given?
92
+ original = @array.to_a.dup
93
+ delete_if { |x| not yield x }
94
+ return nil if self == original
95
+ self
96
+ end
97
+
98
+ # untested
99
+ def keep_if
100
+ return @array.to_enum(:keep_if) unless block_given?
101
+ delete_if { |x| not yield x }
102
+ end
103
+
104
+ # untested
105
+ def count arg = :undefined
106
+ if arg == :undefined
107
+ return @array.length unless block_given?
108
+ counter = 0
109
+ each { |x| counter += 1 if yield x }
110
+ counter
111
+ else
112
+ @array.to_a.count arg
113
+ end
114
+ end
115
+
116
+ def each
117
+ return @array.to_enum(:each) unless block_given?
118
+
119
+ catch_a_break do
120
+ @backward_index, @current_index, @forward_index = -1, 0, 1
121
+ while @current_index < @array.size
122
+ yield @array.at(@current_index)
123
+ increment_indices
124
+ end
125
+
126
+ self
127
+ end
128
+ end
129
+
130
+ def each_index
131
+ return @array.to_enum(:each_index) unless block_given?
132
+
133
+ catch_a_break do
134
+ @backward_index, @current_index, @forward_index = -1, 0, 1
135
+ while @current_index < @array.size
136
+ yield @current_index
137
+ increment_indices
138
+ end
139
+
140
+ self
141
+ end
142
+ end
143
+
144
+ def each_with_index
145
+ return @array.to_enum(:each_with_index) unless block_given?
146
+
147
+ catch_a_break do
148
+ @backward_index, @current_index, @forward_index = -1, 0, 1
149
+ while @current_index < @array.size
150
+ yield @array.at(@current_index), @current_index
151
+ increment_indices
152
+ end
153
+
154
+ self
155
+ end
156
+ end
157
+
158
+ # Should delete_if be smarter / more magical than this?
159
+ # I had considered having it test for whether an element
160
+ # it is going to delete has already been deleted while
161
+ # it was yielded to the iteration block. Doing so would
162
+ # be something of a wild-goose chase but then so is this
163
+ # entire project. I don't know if making it more 'magical'
164
+ # would make its behavior more or less predictable to the
165
+ # end user.
166
+ def delete_if # Rubinius includes `&block` as an argument, but I don't know why
167
+ return @array.to_enum(:delete_if) unless block_given?
168
+
169
+ catch_a_break do
170
+ @backward_index, @current_index, @forward_index = -1, 0, 1
171
+ while @current_index < @array.size
172
+
173
+ # Usage of binding is necessary since this current :delete_if
174
+ # call might be operating inside several levels of nested
175
+ # iteration. If we just used :delete_at here, those higher
176
+ # iteration levels would not be able to adjust their indices
177
+ # to account for the change in array size.
178
+ if yield @array.at(@current_index)
179
+ @progenitor_binding.eval "self.delete_at #{@current_index}"
180
+ end
181
+ increment_indices
182
+ end
183
+
184
+ self
185
+ end
186
+ end
187
+
188
+ # untested
189
+ def reject!
190
+ return @array.to_enum(:reject!) unless block_given?
191
+ original = @array.to_a.dup
192
+ delete_if { |x| yield x }
193
+ return nil if self == original
194
+ self
195
+ end
196
+
197
+ # untested
198
+ def reject
199
+ return @array.to_enum(:reject) unless block_given?
200
+ out = IterableArray.new
201
+ each { |x| out << x unless yield x }
202
+ out
203
+ end
204
+
205
+ # untested
206
+ # need to fix default argument to allow folks to search for nil
207
+ def rindex obj = nil
208
+ if obj.nil?
209
+ return @array.to_enum(:rindex) unless block_given?
210
+
211
+ catch_a_break do
212
+ invert_tracking
213
+ @current_index = @array.size - 1
214
+ @backward_index, @forward_index = @current_index - 1, @current_index + 1
215
+ while @current_index >= 0
216
+ return @current_index if yield @array.at @current_index
217
+
218
+ @current_index = @backward_index
219
+ @forward_index = @current_index + 1
220
+ @backward_index = @current_index - 1
221
+ end
222
+ nil
223
+ end
224
+ else
225
+ @array.rindex obj
226
+ end
227
+ end
228
+
229
+ def reverse_each
230
+ return @array.to_enum(:reverse_each) unless block_given?
231
+
232
+ catch_a_break do
233
+ invert_tracking
234
+ @current_index = @array.size - 1
235
+ @backward_index, @forward_index = @current_index - 1, @current_index + 1
236
+ while @current_index >= 0
237
+ yield @array.at @current_index
238
+
239
+ @current_index = @backward_index
240
+ @forward_index = @current_index + 1
241
+ @backward_index = @current_index - 1
242
+ end
243
+
244
+ self
245
+ end
246
+ end
247
+
248
+ # untested
249
+ def take_while
250
+ return @array.to_enum(:take_while) unless block_given?
251
+ out = IterableArray.new
252
+ each { |x| break unless yield x; out << x }
253
+ out
254
+ end
255
+
256
+ def map
257
+ return @array.dup unless block_given?
258
+ out = IterableArray.new
259
+
260
+ catch_a_break do
261
+ @backward_index, @current_index, @forward_index = -1, 0, 1
262
+ while @current_index < @array.size
263
+ out << yield(@array.at @current_index)
264
+ increment_indices
265
+ end
266
+
267
+ out
268
+ end
269
+ end
270
+
271
+ alias_method :collect, :map
272
+
273
+ def map!
274
+ return to_enum(:map!) unless block_given?
275
+
276
+ catch_a_break do
277
+ @backward_index, @current_index, @forward_index = -1, 0, 1
278
+ while @current_index < @array.size
279
+ # The following verbosity required so that @current_index will be
280
+ # evaluated after any modifications to it by the block.
281
+ temp_value = yield(@array.at @current_index)
282
+ @array[@current_index] = temp_value
283
+ increment_indices
284
+ end
285
+
286
+ self
287
+ end
288
+ end
289
+
290
+ alias_method :collect!, :map!
291
+
292
+ def cycle n = nil, &block
293
+ return @array.to_enum(:cycle, n) unless block_given?
294
+
295
+ catch_a_break do
296
+ if n.nil?
297
+ until @array.empty?
298
+ @backward_index, @current_index, @forward_index = -1, 0, 1
299
+ while @current_index < @array.size
300
+ yield @array.at(@current_index)
301
+ increment_indices
302
+ end
303
+ end
304
+ else
305
+ n.times do
306
+ @backward_index, @current_index, @forward_index = -1, 0, 1
307
+ while @current_index < @array.size
308
+ yield @array.at(@current_index)
309
+ increment_indices
310
+ end
311
+ end
312
+ end
313
+
314
+ nil
315
+ end
316
+ end
317
+
318
+ def index obj = :undefined
319
+ unless block_given?
320
+ # What should I change :undefined to?
321
+ # I can't use null or nil or anything in case the index
322
+ # of null or nil needs to be looked up.
323
+ # Rubinius uses a plain (non-symbol) 'undefined' keyword,
324
+ # but that doesn't seem to work in 1.9.2.
325
+ return @array.index(obj) unless obj == :undefined
326
+ return @array.to_enum(:index)
327
+ end
328
+
329
+ catch_a_break do
330
+ @backward_index, @current_index, @forward_index = -1, 0, 1
331
+ while @current_index < @array.size
332
+ return @current_index if yield @array.at(@current_index)
333
+ increment_indices
334
+ end
335
+
336
+ nil
337
+ end
338
+ end
339
+
340
+ # Have not tested aliased iterators to ensure
341
+ # maximal efficacitic beneficial results
342
+ alias_method :find_index, :index
343
+
344
+ # untested
345
+ def zip *args
346
+ if block_given?
347
+ i = 0
348
+ each do |x|
349
+ out = [x]
350
+ args.each { |arg| out.push arg.at i }
351
+ i += 1
352
+ yield self.class.new out
353
+ end
354
+ nil
355
+ else
356
+ self.class.new(to_a.zip(*args).map { |x| self.class.new x })
357
+ end
358
+ end
359
+
360
+ # untested
361
+ def product *arrays
362
+ return IterableArray.new @array.product(*arrays) unless block_given?
363
+
364
+ each do |x|
365
+ basket = [x].product *arrays
366
+ # means of skipping a whole chunk of yields
367
+ catch :product_next do
368
+ basket.each { |y| yield IterableArray.new y }
369
+ end
370
+ end
371
+ end
372
+
373
+ # Currently only defined for arrays `a` where `a.uniq == a`
374
+ def permutation n = @array.size, &block
375
+ comb_perm_helper :permutation, n, &block
376
+ end
377
+
378
+ # Currently only defined for arrays `a` where `a.uniq == a`
379
+ def combination n, &block
380
+ comb_perm_helper :combination, n, &block
381
+ end
382
+
383
+ # Currently only defined for arrays `a` where `a.uniq == a`
384
+ def repeated_permutation n = @array.size, &block
385
+ comb_perm_helper :repeated_permutation, n, &block
386
+ end
387
+
388
+ # Currently only defined for arrays `a` where `a.uniq == a`
389
+ def repeated_combination n, &block
390
+ comb_perm_helper :repeated_combination, n, &block
391
+ end
392
+
393
+ private
394
+ def comb_perm_helper methd, n
395
+ return @array.to_enum(methd, n) unless block_given?
396
+ @backward_index, @current_index, @forward_index = 0, 0, 0
397
+
398
+ catch_a_break do
399
+ queue = comb_perm_generator methd, to_a, n
400
+ history = []
401
+ until queue.empty?
402
+ element = queue.shift
403
+ previous = to_a.sort
404
+ yield element
405
+ history.push element
406
+ queue = diff_handler queue, history, previous, methd, n unless to_a.sort == previous
407
+ end
408
+ self
409
+ end
410
+ end
411
+
412
+ def comb_perm_generator methd, ary, n
413
+ out = []
414
+ ary.method(methd)[ n, &(->(item) {
415
+ out << item
416
+ }) ]
417
+ out
418
+ end
419
+
420
+ # only for :permutation/:combination and their cousins
421
+ def diff_handler queue, history, previous, methd, n
422
+ deleted_items = previous - self
423
+ new_items = self - previous
424
+ queue = remove_deleted_items queue, deleted_items
425
+ queue = add_new_items queue, history, new_items, methd, n
426
+ queue
427
+ end
428
+
429
+ # only for :permutation/:combination and their cousins
430
+ def add_new_items queue, history, items, methd, n
431
+ new_elements = comb_perm_generator methd, to_a, n
432
+ new_elements -= history
433
+ queue += new_elements
434
+ queue
435
+ end
436
+
437
+ # only for :permutation/:combination and their cousins
438
+ def remove_deleted_items queue, items
439
+ items.each do |item|
440
+ queue.delete_if { |x| x.include? item }
441
+ end
442
+ queue
443
+ end
444
+ # end
445
+ end
@@ -0,0 +1,31 @@
1
+ class IterableArray
2
+ # module PlainModifiers
3
+ private
4
+ def define_plain_modifiers
5
+ class << self
6
+ def delete_at location
7
+ # Flip to positive and weed out out of bounds
8
+ location += @array.size if location < 0
9
+ return nil unless location.between? 0, @array.size.pred
10
+
11
+ @forward_index -= 1 if location < @forward_index
12
+ @current_index -= 1 if location < @current_index - @tracking
13
+ @backward_index -= 1 if location < @backward_index
14
+
15
+ @array.delete_at location
16
+ end
17
+
18
+ # currently untested
19
+ def delete obj
20
+ n = count obj
21
+ if n.zero?
22
+ return yield obj if block_given?
23
+ nil
24
+ else
25
+ n.times { delete_at index obj }
26
+ obj
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,194 @@
1
+ class IterableArray
2
+ # module SpecialAccessors
3
+ def << arg
4
+ @array << arg
5
+ self
6
+ end
7
+
8
+ def concat arg
9
+ arg.each { |x| @array << x }
10
+ self
11
+ end
12
+
13
+ def [](arg1, arg2=nil)
14
+ case arg1
15
+
16
+ when Fixnum
17
+ return @array.at(arg1) unless arg2
18
+
19
+ IterableArray.new @array[arg1, arg2]
20
+
21
+ when Range
22
+ IterableArray.new @array[arg1]
23
+ end
24
+ end
25
+
26
+ alias_method :slice, :[]
27
+
28
+ def drop n
29
+ IterableArray.new @array.drop n
30
+ end
31
+
32
+ # untested
33
+ def dup
34
+ IterableArray.new @array.to_a.dup
35
+ end
36
+
37
+ def first n = nil
38
+ return @array.first if n == nil
39
+ IterableArray.new @array.first(n)
40
+ end
41
+
42
+ def last n = nil
43
+ return @array.last if n == nil
44
+ IterableArray.new @array.last(n)
45
+ end
46
+
47
+ def push *args
48
+ @array.push *args
49
+ self
50
+ end
51
+
52
+ # untested
53
+ def compact
54
+ IterableArray.new @array.compact
55
+ end
56
+
57
+ # It looks like Array#assoc and Array#rassoc return
58
+ # Array versions of their elements when they find a
59
+ # match. I'm not sure why that is... You'd think it
60
+ # would be more duck-typish to maintain the object's
61
+ # class, right? I dunno why it has to be so sad.
62
+ def assoc obj
63
+ @array.each do |x|
64
+ if x.respond_to? :at and x.at(0) == obj
65
+ return x
66
+ end
67
+ end
68
+
69
+ nil
70
+ end
71
+
72
+ # See note above assoc.
73
+ def rassoc obj
74
+ @array.each do |elem|
75
+ if elem.respond_to? :at and elem.at(1) == obj
76
+ return elem
77
+ end
78
+ end
79
+
80
+ nil
81
+ end
82
+
83
+ # Not defined since Array#nitems is not defined in 1.9.2+
84
+ # def nitems
85
+ # end
86
+
87
+ def & arg
88
+ IterableArray.new(@array & arg.to_a)
89
+ end
90
+
91
+ def | arg
92
+ IterableArray.new(@array | arg.to_a)
93
+ end
94
+
95
+ def + arg
96
+ IterableArray.new(@array + arg)
97
+ end
98
+
99
+ def - arg
100
+ IterableArray.new(@array - arg)
101
+ end
102
+
103
+ def * arg
104
+ return IterableArray.new @array * arg if arg.kind_of? Fixnum
105
+ @array * arg
106
+ end
107
+
108
+ def <=>(other)
109
+ return @array <=> other.to_a if other.kind_of? IterableArray
110
+ @array <=> other
111
+ end
112
+
113
+ def values_at *args
114
+ out = IterableArray.new
115
+ args.each do |arg|
116
+ out += @array.values_at arg
117
+ end
118
+ out
119
+ end
120
+
121
+ alias_method :indices, :values_at
122
+ alias_method :indexes, :values_at
123
+
124
+ # TODO need Mspec test...ran into problems with this one
125
+ def sample arg = nil
126
+ return IterableArray.new(@array.sample arg) unless arg.nil?
127
+ @array.sample
128
+ end
129
+
130
+ # :eql? returns true only when array contents are the same and
131
+ # both objects are IterableArray instances
132
+ def eql? arg
133
+ (arg.class == IterableArray) and
134
+ (self == arg.to_a)
135
+ end
136
+
137
+ # untested
138
+ def flatten level = -1
139
+ IterableArray.new @array.flatten(level)
140
+ end
141
+
142
+ # untested
143
+ def reverse
144
+ IterableArray.new @array.reverse
145
+ end
146
+
147
+ # untested
148
+ def rotate n = 1
149
+ IterableArray.new @array.rotate(n)
150
+ end
151
+
152
+ # untested
153
+ def replace *args
154
+ @array.replace *args
155
+ self
156
+ end
157
+
158
+ def sort
159
+ IterableArray.new @array.sort
160
+ end
161
+
162
+ def shuffle
163
+ IterableArray.new @array.shuffle
164
+ end
165
+
166
+ def swap *args
167
+ IterableArray.new @array.swap(*args)
168
+ end
169
+
170
+ def swap_indices *args
171
+ IterableArray.new @array.swap_indices(*args)
172
+ end
173
+
174
+ # untested
175
+ def take n
176
+ IterableArray.new @array.take(n)
177
+ end
178
+
179
+ # untested
180
+ # Note: All internal arrays will also be IterableArrays.
181
+ # Any point at all to this? One doubts...
182
+ def transpose
183
+ # So. Pointless. Blerg.
184
+ IterableArray.new(@array.transpose.map do |x|
185
+ IterableArray.new x
186
+ end)
187
+ end
188
+
189
+ # untested
190
+ def uniq
191
+ IterableArray.new @array.uniq
192
+ end
193
+ # end
194
+ end
@@ -0,0 +1,226 @@
1
+ class IterableArray
2
+ # module SpecialModifiersIterating
3
+ private
4
+ def define_special_modifiers_iterating
5
+ class << self
6
+ def clear
7
+ @backward_index -= @current_index
8
+ @forward_index -= @current_index
9
+ @current_index = 0
10
+ @array.clear
11
+ end
12
+
13
+ # untested
14
+ def compact!
15
+ delete_if &:nil?
16
+ self
17
+ end
18
+
19
+ def shift n = nil
20
+ return self.delete_at(0) if n == nil
21
+
22
+ out = IterableArray.new
23
+ [ n, @array.length ].min.times { out << self.delete_at(0) }
24
+ out
25
+ end
26
+
27
+ def insert location, *items
28
+ location = size + location if location < 0
29
+
30
+ @array.insert location, *items # I put this here rather than at the end
31
+ # so that any possible location error is raised
32
+ # before modifying the indices.
33
+
34
+ center_indices_at(@current_index + items.size) if location <= @current_index
35
+
36
+ self
37
+ end
38
+
39
+ # untested
40
+ def move element, to
41
+ from = index element
42
+ move_from from, to
43
+ end
44
+
45
+ # untested
46
+ def move_from from, to
47
+ if from == @current_index then
48
+ center_indices_at to
49
+ elsif from < @current_index and to >= @current_index
50
+ center_indices_at(@current_index - 1)
51
+ elsif from > @current_index and to <= @current_index
52
+ center_indices_at(@current_index + 1)
53
+ end
54
+ @array.move_from from, to
55
+ end
56
+
57
+ # untested
58
+ def flatten! level = -1
59
+ return self if level.zero?
60
+ @forward_index = to_a[0...@forward_index].flatten(level).size
61
+ @backward_index = @forward_index - 1
62
+ @current_index = (@backward_index + @tracking.abs + @tracking).to_int
63
+ @array.flatten! level
64
+ self
65
+ end
66
+
67
+ def slice! start, length=:undefined
68
+ if length.equal? :undefined
69
+ if start.kind_of? Range
70
+ out = IterableArray.new @array.slice(start)
71
+ first, last = start.first, start.last
72
+ first += @array.length if first < 0
73
+ last += @array.length if last < 0
74
+ if first <= last
75
+ length = last - first
76
+ length += 1 unless start.exclude_end?
77
+ length.times { delete_at first }
78
+ end
79
+ else
80
+ out = delete_at start
81
+ end
82
+ else
83
+ out = IterableArray.new @array.slice(start, length)
84
+ start += @array.length if start < 0
85
+ length.times { delete_at start }
86
+ end
87
+ out
88
+ end
89
+
90
+ def unshift *args
91
+ insert 0, *args
92
+ self
93
+ end
94
+
95
+ def pop n = nil
96
+ return delete_at(size - 1) if n.nil?
97
+
98
+ out = IterableArray.new []
99
+ n.times { out.unshift pop }
100
+ out
101
+ end
102
+
103
+ def reverse!
104
+ @backward_index, @forward_index =
105
+ @array.size - 1 - @forward_index,
106
+ @array.size - 1 - @backward_index
107
+
108
+ # @current_index = @backward_index + 1
109
+ @current_index = @array.size - 1 - @current_index
110
+
111
+ @current_index = case @current_index
112
+ when @backward_index then @forward_index
113
+ when @forward_index then @backward_index
114
+ else @current_index
115
+ end
116
+
117
+ invert_tracking
118
+
119
+ @array.reverse!
120
+ self
121
+ end
122
+
123
+ # partially tested (not tested nested)
124
+ def rotate! n = 1
125
+ n %= size
126
+ return self if n.zero?
127
+ new_index = (@current_index - n) % size
128
+ center_indices_at new_index
129
+ @array.rotate! n
130
+ end
131
+
132
+ # Still need to implement with block
133
+ # `#sort!` modification is iterative-aware, but the instance
134
+ # of IterableArray should not be modified from within a `#sort!`
135
+ # block:
136
+ # quoth the pickaxe: _arr_ is effectively frozen while a sort is in progress
137
+ def sort!
138
+ return @array.sort! unless @current_index.between? 0, @array.size.pred
139
+
140
+ current_item = @array.at @current_index
141
+ offset = @array.to_a[0...@current_index].count current_item
142
+
143
+ @array.sort!
144
+
145
+ center_indices_at (@array.to_a.index(current_item) + offset)
146
+
147
+ self
148
+ end
149
+
150
+ # untested
151
+ # `#sort_by!` modification is iterative-aware, but the instance
152
+ # of IterableArray should not be modified from within a `#sort_by!`
153
+ # block:
154
+ # quoth the pickaxe: _arr_ is effectively frozen while a sort is in progress
155
+ def sort_by! &block
156
+ return @array.to_enum(:sort_by!) unless block_given?
157
+
158
+ current_item = @array.at @current_index
159
+ offset = @array.to_a[0...@current_index].count current_item
160
+
161
+ @array.sort_by! &block
162
+
163
+ center_indices_at (@array.to_a.index(current_item) + offset)
164
+
165
+ self
166
+ end
167
+
168
+ def shuffle!
169
+ return @array.shuffle! unless @current_index.between? 0, @array.size.pred
170
+
171
+ current_item = @array.at @current_index
172
+ count = @array.to_a.count current_item
173
+
174
+ @array.shuffle!
175
+
176
+ locations = []
177
+ @array.to_a.each_with_index { |item, location| locations << location if item == current_item }
178
+
179
+ center_indices_at locations.sample
180
+
181
+ self
182
+ end
183
+
184
+ def swap! *args
185
+ args.map! { |x| index x }
186
+ swap_indices! *args
187
+ end
188
+
189
+ def swap_indices! *args
190
+ args.inject { |i1, i2| swap_2_indices!(i1, i2); i2 }
191
+ self
192
+ end
193
+ alias_method :swap_indexes!, :swap_indices!
194
+
195
+ # untested
196
+ def uniq!
197
+ basket = []
198
+ delete_if do |x|
199
+ if basket.include? x
200
+ true
201
+ else
202
+ basket << x
203
+ false
204
+ end
205
+ end
206
+ self
207
+ end
208
+
209
+ protected
210
+ def swap_2_indices! arg1, arg2
211
+ temp_holder = @current_index
212
+ center_indices_at arg1 if temp_holder == arg2
213
+ center_indices_at arg2 if temp_holder == arg1
214
+ @array.swap_indices! arg1, arg2
215
+ end
216
+
217
+ private
218
+ def center_indices_at new_index
219
+ movement = @current_index - new_index
220
+ @current_index -= movement
221
+ @forward_index -= movement
222
+ @backward_index -= movement
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,104 @@
1
+ class IterableArray
2
+ module SpecialModifiersNoniterating
3
+ # untested
4
+ def compact!
5
+ @array.compact!
6
+ self
7
+ end
8
+
9
+ def clear
10
+ @array.clear
11
+ self
12
+ end
13
+
14
+ def shift n = nil
15
+ return @array.shift if n.nil?
16
+ IterableArray.new @array.shift(n)
17
+ end
18
+
19
+ def insert location, *items
20
+ @array.insert location, *items
21
+ self
22
+ end
23
+
24
+ # untested
25
+ def move element, to
26
+ @array.move element, to
27
+ self
28
+ end
29
+
30
+ # untested
31
+ def move_from from, to
32
+ @array.move_from from, to
33
+ self
34
+ end
35
+
36
+ # untested
37
+ def flatten! level = -1
38
+ @array.flatten! level
39
+ self
40
+ end
41
+
42
+ def pop n = nil
43
+ return @array.pop if n.nil?
44
+ IterableArray.new(@array.pop n)
45
+ end
46
+
47
+ def reverse!
48
+ @array.reverse!
49
+ self
50
+ end
51
+
52
+ # untested
53
+ def rotate! n = 1
54
+ @array.rotate! n
55
+ self
56
+ end
57
+
58
+ def sort!
59
+ @array.sort!
60
+ self
61
+ end
62
+
63
+ # untested
64
+ def sort_by! &block
65
+ return @array.to_enum(:sort_by!) unless block_given?
66
+ @array.sort_by! &block
67
+ self
68
+ end
69
+
70
+ def shuffle!
71
+ @array.shuffle!
72
+ self
73
+ end
74
+
75
+ def slice! *args
76
+ if args.length == 2 or args.at(1).kind_of? Range
77
+ IterableArray.new @array.slice!(*args)
78
+ else
79
+ @array.slice!(*args)
80
+ end
81
+ end
82
+
83
+ def unshift *args
84
+ @array.insert 0, *args
85
+ self
86
+ end
87
+
88
+ def swap! *args
89
+ @array.swap! *args
90
+ self
91
+ end
92
+
93
+ def swap_indices! *args
94
+ @array.swap_indices! *args
95
+ self
96
+ end
97
+
98
+ # untested
99
+ def uniq!
100
+ @array.uniq!
101
+ self
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,47 @@
1
+ module Swapable
2
+ def to_iter
3
+ IterableArray.new self
4
+ end
5
+
6
+ def to_a
7
+ self
8
+ end
9
+
10
+ def swap_2_indices! arg1, arg2
11
+ temper = at arg1
12
+ self[arg1] = at arg2
13
+ self[arg2] = temper
14
+ self
15
+ end
16
+ protected :swap_2_indices!
17
+
18
+ def swap! *args
19
+ args.map! { |x| index x }
20
+ swap_indices! *args
21
+ end
22
+
23
+ def swap_indices! *args
24
+ args.inject { |i1, i2| swap_2_indices! i1, i2; i2 }
25
+ self
26
+ end
27
+ alias_method :swap_indexes!, :swap_indices!
28
+
29
+ def swap *args
30
+ dup.extend(Swapable).swap! *args
31
+ end
32
+
33
+ def swap_indices *args
34
+ dup.extend(Swapable).swap_indices! *args
35
+ end
36
+ alias_method :swap_indexes, :swap_indices
37
+
38
+ def move_from from, to
39
+ element = delete_at from
40
+ insert to, element
41
+ end
42
+
43
+ def move element, to
44
+ delete_at index element
45
+ insert to, element
46
+ end
47
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: iterable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6.pre
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Scott L Steele
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-13 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ''
15
+ email: ScottLSteele@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/iterable_array/iterator_specials.rb
21
+ - lib/iterable_array/special_modifiers_noniterating.rb
22
+ - lib/iterable_array/special_modifiers_iterating.rb
23
+ - lib/iterable_array/special_accessors.rb
24
+ - lib/iterable_array/plain_modifiers.rb
25
+ - lib/iterable_array/iterators.rb
26
+ - lib/swapable.rb
27
+ - lib/iterable.rb
28
+ - lib/iterable_array.rb
29
+ homepage: https://github.com/scooter-dangle/iterable
30
+ licenses:
31
+ - ''
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>'
46
+ - !ruby/object:Gem::Version
47
+ version: 1.3.1
48
+ requirements: []
49
+ rubyforge_project:
50
+ rubygems_version: 1.8.10
51
+ signing_key:
52
+ specification_version: 3
53
+ summary: Provides a fully iterable array object
54
+ test_files: []
55
+ has_rdoc: