random-accessible 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ = random-accessible
2
+
3
+ RandomAccessible mixin provides all methods of Array to your classes (regard as high-functioning edition of Enumerable).
4
+ As Enumerable mixin requests "each" method, RandomAccessible requests methods below.
5
+ - size (same as Array#size)
6
+ - read_access (similar to Array#[])
7
+ - replace_access (similar to Array#[]=)
8
+ - shrink (similar to Array#pop)
9
+
10
+ size method returns the number of objects which the object contains.
11
+ Note that this method must behave same as Array's.
12
+
13
+ read_access(pos) is similar to Array#[]. But the module guarantees that the argument is an Integer larger than or equal 0 and less than its size.
14
+
15
+ replace_access(pos, val) is similar to Array#[]=. The module guarantees that the argument pos is an Integer and 0 <= pos < size.
16
+
17
+ shrink is similar to Array#pop. But the method can return anything.
18
+
19
+ == Installation
20
+
21
+ gem install random-accessible
22
+
23
+ == Limitation
24
+
25
+ Behavior of RandomAccessible#eql? is different from Array's.
26
+ The method always returns false if the argument does not include RandomReadable (RandomAccessible includes RandomReadable). Also note that Array#eql? returns false if the arguent is RandomAccessible.
27
+
28
+ == License
29
+
30
+ This library is distributed under the dual license of the Ruby license (in the narrow sense) and the 2-clause BSD license. Please see http://www.ruby-lang.org and BSDL.
31
+ Copyright (c) 2011, Natsuki Kawai.
@@ -0,0 +1,598 @@
1
+ # Author:: Natsuki Kawai (natsuki.kawai@gmail.com)
2
+ # Copyright:: Copyright 2011 Natsuki Kawai
3
+ # License:: 2-clause BSDL or Ruby's
4
+
5
+
6
+ require 'common-traits'
7
+
8
+ # RandomReadable mixin provides non-destructive instance methods of Array.
9
+ # The class must provide one or more methods from read-accessor group ([], at, read_access).
10
+ # The class may provide one size-provider (size, length).
11
+ # read_access(pos) is similar to "at" method, but the module guarantees the argument is positive.
12
+ # And if the class provides size or length methods, the argument is less than size or length.
13
+ # If the class does not provide size-provider, some of the methods of the module
14
+ # raises NotImplementedError. Please see the document of each method.
15
+ module RandomReadable
16
+
17
+ include RandomAccessible::CommonTraits
18
+ include Enumerable
19
+
20
+ # Returns all elements of the class as an Array.
21
+ # This method evaluates all elements of the class.
22
+ # This method raises NotImplementedError
23
+ # if the class provides neither size nor length method.
24
+ def to_ary
25
+ Enumerator.new do |y|
26
+ size.times do |i|
27
+ y << at(i)
28
+ end
29
+ end.to_a
30
+ end
31
+
32
+ alias :to_a :to_ary
33
+
34
+ def delegate_to_array(name, *args, &block)
35
+ to_ary.send(name, *args, &block)
36
+ end
37
+ private :delegate_to_array
38
+
39
+ # Same as Array's.
40
+ # This method evaluates all elements of the class.
41
+ # This method raises NotImplementedError
42
+ # if the class provides neither size nor length method.
43
+ def &(other)
44
+ delegate_to_array(:&, other)
45
+ end
46
+
47
+ # Same as Array's.
48
+ # This method evaluates all elements of the class.
49
+ # This method raises NotImplementedError
50
+ # if the class provides neither size nor length method.
51
+ def *(arg)
52
+ delegate_to_array(:*, arg)
53
+ end
54
+
55
+ # Same as Array's.
56
+ # This method evaluates all elements of the class.
57
+ # This method raises NotImplementedError
58
+ # if the class provides neither size nor length method.
59
+ def +(other)
60
+ delegate_to_array(:+, other)
61
+ end
62
+
63
+ # Same as Array's.
64
+ # This method evaluates all elements of the class.
65
+ # This method raises NotImplementedError
66
+ # if the class provides neither size nor length method.
67
+ def -(other)
68
+ delegate_to_array(:-, other)
69
+ end
70
+
71
+ # Same as Array's.
72
+ # This method evaluates elements needed to get results.
73
+ # This method raises NotImplementedError
74
+ # if the class provides neither size nor length method.
75
+ def <=>(other)
76
+ min_size = [size, other.size].min
77
+ min_size.times do |i|
78
+ res = self[i] <=> other[i]
79
+ return res if res != 0
80
+ end
81
+ return size <=> other.size
82
+ end
83
+
84
+ # Same as Array's if the class provides size method.
85
+ # Same as Object's if not.
86
+ # This method evaluates elements needed to get results.
87
+ def ==(other)
88
+ return super unless has_size?
89
+ return false if size != other.size
90
+ size.times do |i|
91
+ return false if self[i] != other[i]
92
+ end
93
+
94
+ return true
95
+ end
96
+
97
+ # This method is a read-accessor (see README).
98
+ # If you overrides this method, provide same function as Array's.
99
+ # Same as Array's.
100
+ # If the argument is one Integer, this method evaluates one element.
101
+ # If the argument is a Range or start/length, this method evaluates
102
+ # elements in the Range or start/length.
103
+ # This method raises NotImplementedError
104
+ # if the class provides neither size nor length method, and the argument is minus.
105
+ def [](*args)
106
+ if args.size >= 3 || args.size == 0
107
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 1..2)"
108
+ end
109
+
110
+ if args.size == 2
111
+ start = args[0].to_int
112
+ len = args[1].to_int
113
+ if has_size?
114
+ return nil if start < -size || size < start
115
+ return nil if len < 0
116
+ return Enumerator.new do |y|
117
+ len.times do |i|
118
+ y << at(start + i) if start + i < size
119
+ end
120
+ end.to_a
121
+ else
122
+ return Enumerator.new do |y|
123
+ len.times do |i|
124
+ y << at(start + i)
125
+ end
126
+ end.to_a
127
+ end
128
+ elsif args[0].is_a? Range
129
+ range = args[0]
130
+ first = range.first
131
+ last = range.last
132
+
133
+ if has_size?
134
+ first += size if first < 0
135
+ last += size if last < 0
136
+ last -= 1 if range.exclude_end?
137
+ if first == size || (first == 0 && last < 0)
138
+ return []
139
+ elsif first < 0 || size < first
140
+ return nil
141
+ end
142
+ return Enumerator.new do |y|
143
+ (first..last).each do |i|
144
+ y << at(i) if 0 <= i && i < size
145
+ end
146
+ end.to_a
147
+ else
148
+ range.map do |i|
149
+ at(i)
150
+ end
151
+ end
152
+ else
153
+ at(args[0])
154
+ end
155
+ end
156
+
157
+ # Same as Array's.
158
+ # This method sequentially evaluates the elements.
159
+ # Note that this method loops infinitely
160
+ # if the class provides neither size nor lenght method.
161
+ def assoc(key)
162
+ enum = has_size? ? :each : :cycle
163
+ send(enum) do |el|
164
+ if el.respond_to?(:[]) && !el.empty? && el[0] == key
165
+ return el
166
+ end
167
+ end
168
+ return nil
169
+ end
170
+
171
+ # This method is a read-accessor (see README).
172
+ # If you overrides this method, provide same function as Array's.
173
+ # Same as Array's.
174
+ # This method evaluates one element.
175
+ # even if the argument is minus or out-of-range.
176
+ def at(pos)
177
+ pos = pos.to_int
178
+
179
+ if 0 <= pos && !has_size?
180
+ return read_access(pos)
181
+ elsif 0 <= pos && pos < size
182
+ return read_access(pos)
183
+ elsif -size <= pos && pos < 0
184
+ return read_access(size + pos)
185
+ else
186
+ return nil
187
+ end
188
+ end
189
+
190
+ # Need not to override Object#clone and Object#dup
191
+
192
+ # Same as Array's.
193
+ # This method evaluates all elements of the class.
194
+ # This method raises NotImplementedError
195
+ # if the class provides neither size nor length method.
196
+ def combination(n, &block)
197
+ delegate_to_array(:combination, n, &block)
198
+ end
199
+
200
+ # Same as Array's.
201
+ # This method evaluates all elements of the class.
202
+ # This method raises NotImplementedError
203
+ # if the class provides neither size nor length method.
204
+ def compact
205
+ Enumerator.new do |y|
206
+ each do |el|
207
+ y << el unless el.nil?
208
+ end
209
+ end.to_a
210
+ end
211
+
212
+ # Same as Array's.
213
+ # This method sequentially evaluates the elements of the class.
214
+ def cycle
215
+ if has_size?
216
+ super
217
+ else
218
+ i = 0
219
+ loop do
220
+ yield at(i)
221
+ i += 1
222
+ end
223
+ end
224
+ end
225
+
226
+ # Same as Array's.
227
+ # This method sequentially evaluates the elements of the class.
228
+ # This method or the Enumerator raises NotImplementedError
229
+ # if the class provides neither size nor length method.
230
+ def each(&block)
231
+ if block.nil?
232
+ return Enumerator.new do |y|
233
+ size.times do |i|
234
+ y << self[i]
235
+ end
236
+ end
237
+ else
238
+ size.times do |i|
239
+ block.call(self[i])
240
+ end
241
+ return self
242
+ end
243
+ end
244
+
245
+ # if the object provides size or length, this method is same as Array's
246
+ # and evaluates minimum elements needed to get results.
247
+ # If not, this method is same as Object's and evaluates no element.
248
+ def eql?(other)
249
+ return false unless self.class.eql?(other.class)
250
+ return super(other) unless has_size?
251
+
252
+ each_index do |i|
253
+ return false unless at(i).eql?(other.at(i))
254
+ end
255
+ return true
256
+ end
257
+
258
+ # Same as Array's.
259
+ # If the argument is an index, this method evaluates one element.
260
+ # If the argument is start/length, this method evaluates
261
+ # elements between the start/length.
262
+ # This method does not accept a minus index
263
+ # if the class provides neither size nor length method.
264
+ def fetch(nth, *args, &block)
265
+ if args.size >= 2
266
+ raise ArgumentError, "wrong number of arguments (#{args.size + 1} for 1..2)"
267
+ end
268
+
269
+ if has_size? && (nth < -size || size <= nth)
270
+ if block != nil
271
+ # TODO: Warn if ifnone value is present.
272
+ return block.call
273
+ elsif args.size == 1
274
+ return args[0]
275
+ else
276
+ raise IndexError,
277
+ "index #{nth} outsize of the random readable object " \
278
+ "bounds: #{-size}...#{size}"
279
+ end
280
+ else
281
+ return at(nth)
282
+ end
283
+ end
284
+
285
+ # Same as Array's.
286
+ # If the argument is an index, this method evaluates one element.
287
+ # If the argument is start/length, this method evaluates
288
+ # elements between the start/length.
289
+ def first(*args)
290
+ if args.size >= 2
291
+ raise ArgumentError, "wrong number of arguments (#{args.size + 1} for 1..2)"
292
+ end
293
+
294
+ if args.size == 1
295
+ width = args[0]
296
+ width = [width, size].min if has_size?
297
+
298
+ return self[0...width]
299
+ else
300
+ return at(0)
301
+ end
302
+ end
303
+
304
+ # Same as Array's.
305
+ # This method evaluates all elements of the class.
306
+ # This method raises NotImplementedError
307
+ # if the class provides neither size nor length method.
308
+ def flatten(lv = nil)
309
+ delegate_to_array(:flatten, lv)
310
+ end
311
+
312
+ # Same as Array's and evaluates all elements of the class
313
+ # if the class provides size or length method.
314
+ # Same as Object's if not.
315
+ def hash
316
+ return super unless has_size?
317
+
318
+ res = 0
319
+ each do |el|
320
+ res += el.hash
321
+ end
322
+ return res
323
+ end
324
+
325
+ def include?(val = nil, &block)
326
+ !!index(val, &block)
327
+ end
328
+
329
+ # Same as Array's.
330
+ # This method evaluates the elements sequentially.
331
+ # This method raises NotImplementedError
332
+ # if the class provides neither size nor length method.
333
+ def index(val = nil, &block)
334
+ # needs size
335
+ if block.nil?
336
+ each_with_index do |el, index|
337
+ return index if el == val
338
+ end
339
+ else
340
+ each_with_index do |el, index|
341
+ return index if block.call(el)
342
+ end
343
+ end
344
+ return nil
345
+ end
346
+
347
+ # indexes is not defined on Ruby 1.9.
348
+
349
+ # Same as Array's and evaluates all elements of the class
350
+ # if the class provides size or length method.
351
+ # Same as Object's if not.
352
+ def to_s
353
+ return super unless has_size?
354
+
355
+ to_ary.to_s
356
+ end
357
+
358
+ # Same as Array's and evaluates all elements of the class
359
+ # if the class provides size or length method.
360
+ # Same as Object's if not.
361
+ def inspect
362
+ return super unless has_size?
363
+
364
+ to_ary.inspect
365
+ end
366
+
367
+ # Same as Array's.
368
+ # This method evaluates all elements of the class.
369
+ # This method raises NotImplementedError
370
+ # if the class provides neither size nor length method.
371
+ def join(sep = $,)
372
+ to_ary.join(sep)
373
+ end
374
+
375
+ # Same as Array's.
376
+ # This method evaluates minimum elements of the class.
377
+ # This method raises NotImplementedError
378
+ # if the class provides neither size nor length method.
379
+ def last(n = nil)
380
+ if n.nil?
381
+ at(size - 1)
382
+ else
383
+ n = size if n > size
384
+ Enumerator.new do |y|
385
+ n.times do |i|
386
+ y << at(size - n + i)
387
+ end
388
+ end.to_a
389
+ end
390
+ end
391
+
392
+ # Same as Array's.
393
+ # This method evaluates all elements of the class.
394
+ # (TODO: Stop evaluating unnecessary elelments)
395
+ # This method raises NotImplementedError
396
+ # if the class provides neither size nor length method.
397
+ def pack(template)
398
+ delegate_to_array(:pack, template)
399
+ end
400
+
401
+ # Same as Array's.
402
+ # This method evaluates all elements of the class.
403
+ # This method raises NotImplementedError
404
+ # if the class provides neither size nor length method.
405
+ def permutation(n, &block)
406
+ delegate_to_array(:permutation, n, &block)
407
+ end
408
+
409
+ # Same as Array's.
410
+ # This method evaluates all elements of the class.
411
+ # This method raises NotImplementedError
412
+ # if the class provides neither size nor length method.
413
+ def product(*lists, &block)
414
+ delegate_to_array(:product, *lists, &block)
415
+ end
416
+
417
+ # Same as Array's.
418
+ # This method evaluates minimum elements of the class.
419
+ # This method raises NotImplementedError
420
+ # if the class provides neither size nor length method.
421
+ def rassoc(obj)
422
+ each do |el|
423
+ if el.respond_to?(:[]) && el.size >= 2 && el[1] == obj
424
+ return el
425
+ end
426
+ end
427
+ return nil
428
+ end
429
+
430
+ # Same as Array's.
431
+ # This method evaluates all elements of the class.
432
+ # This method raises NotImplementedError
433
+ # if the class provides neither size nor length method.
434
+ def repeated_combination(n, &block)
435
+ delegate_to_array(:repeated_combination, n, &block)
436
+ end
437
+
438
+ # Same as Array's.
439
+ # This method evaluates all elements of the class.
440
+ # This method raises NotImplementedError
441
+ # if the class provides neither size nor length method.
442
+ def repeated_permutation(n, &block)
443
+ delegate_to_array(:repeated_permutation, n, &block)
444
+ end
445
+
446
+ # Same as Array's.
447
+ # This method evaluates all elements of the class.
448
+ # This method raises NotImplementedError
449
+ # if the class provides neither size nor length method.
450
+ def reverse
451
+ delegate_to_array(:reverse)
452
+ end
453
+
454
+ # Same as Array's.
455
+ # This method evaluates elements of the class sequentially.
456
+ # This method raises NotImplementedError
457
+ # if the class provides neither size nor length method.
458
+ def reverse_each(&block)
459
+ # Needs size.
460
+ if block.nil?
461
+ Enumerator.new do |y|
462
+ (size - 1).downto 0 do |i|
463
+ y << at(i)
464
+ end
465
+ end
466
+ else
467
+ (size - 1).downto 0 do |i|
468
+ yield at(i)
469
+ end
470
+ end
471
+ end
472
+
473
+ # Same as Array's.
474
+ # This method evaluates minimum elements of the class.
475
+ # This method raises NotImplementedError
476
+ # if the class provides neither size nor length method.
477
+ def rindex(val = nil, &block)
478
+ i = 0
479
+ if block.nil?
480
+ reverse_each do |el|
481
+ i += 1
482
+ return size - i if el == val
483
+ end
484
+ else
485
+ reverse_each do |el|
486
+ i += 1
487
+ return size - i if block.call(el)
488
+ end
489
+ end
490
+ return nil
491
+ end
492
+
493
+ # Same as Array's.
494
+ # This method evaluates all elements of the class.
495
+ # This method raises NotImplementedError
496
+ # if the class provides neither size nor length method.
497
+ def rotate(cnt = 1)
498
+ delegate_to_array(:rotate, cnt)
499
+ end
500
+
501
+ # Same as Array's.
502
+ # This method evaluates one elements of the class if there is no argument.
503
+ # This method raises NotImplementedError
504
+ # if the class provides neither size nor length method.
505
+ def sample(*args)
506
+ # Needs size.
507
+ if args.size >= 2
508
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 1..2)"
509
+ end
510
+
511
+ if args.size == 1
512
+ n = [args[0].to_int, size].min
513
+ return Enumerator.new do |y|
514
+ each_index do |i|
515
+ if n > 0 && rand(size - i) < n
516
+ y << at(i)
517
+ n -= 1
518
+ end
519
+ end
520
+ end.to_a.shuffle
521
+ else
522
+ if size == 0
523
+ return nil
524
+ else
525
+ return at(rand(size))
526
+ end
527
+ end
528
+ end
529
+
530
+ # Same as Array's.
531
+ # This method evaluates all elements of the class.
532
+ # This method raises NotImplementedError
533
+ # if the class provides neither size nor length method.
534
+ def shuffle
535
+ delegate_to_array(:shuffle)
536
+ end
537
+
538
+ alias :slice :[]
539
+
540
+ # sort is defined in Enumerable.
541
+
542
+ # Same as Array's.
543
+ # This method evaluates all elements of the class.
544
+ # This method raises NotImplementedError
545
+ # if the class provides neither size nor length method.
546
+ def transpose
547
+ delegate_to_array(:transpose)
548
+ end
549
+
550
+ # Same as Array's.
551
+ # This method evaluates all elements of the class.
552
+ # This method raises NotImplementedError
553
+ # if the class provides neither size nor length method.
554
+ def uniq(&block)
555
+ delegate_to_array(:uniq, &block)
556
+ end
557
+
558
+ # Same as Array's.
559
+ # This method evaluates minimum elements of the class.
560
+ # The arguments must not be negative values
561
+ # if the class does not provide size or length method.
562
+ def values_at(*selectors)
563
+ Enumerator.new do |y|
564
+ selectors.each do |s|
565
+ if s.is_a? Range
566
+ subary = self[s]
567
+ unless subary.nil?
568
+ self[s].each do |el|
569
+ y << el
570
+ end
571
+ end
572
+ if has_size? && !s.exclude_end? && s.include?(size)
573
+ y << nil
574
+ end
575
+ else
576
+ y << self[s.to_int]
577
+ end
578
+ end
579
+ end.to_a
580
+ end
581
+
582
+ # Same as Array's.
583
+ # This method evaluates all elements of the class.
584
+ # This method raises NotImplementedError
585
+ # if the class provides neither size nor length method.
586
+ def zip(*lists, &block)
587
+ delegate_to_array(:zip, *lists, &block)
588
+ end
589
+
590
+ # Same as Array's.
591
+ # This method evaluates all elements of the class.
592
+ # This method raises NotImplementedError
593
+ # if the class provides neither size nor length method.
594
+ def |(other)
595
+ delegate_to_array(:|, other)
596
+ end
597
+
598
+ end