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.
- data/lib/iterable.rb +2 -0
- data/lib/iterable_array.rb +94 -0
- data/lib/iterable_array/iterator_specials.rb +68 -0
- data/lib/iterable_array/iterators.rb +445 -0
- data/lib/iterable_array/plain_modifiers.rb +31 -0
- data/lib/iterable_array/special_accessors.rb +194 -0
- data/lib/iterable_array/special_modifiers_iterating.rb +226 -0
- data/lib/iterable_array/special_modifiers_noniterating.rb +104 -0
- data/lib/swapable.rb +47 -0
- metadata +55 -0
data/lib/iterable.rb
ADDED
@@ -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
|
data/lib/swapable.rb
ADDED
@@ -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:
|