Temporary tag for this failure. Updated CI spec coming.
[rbx.git] / kernel / core / enumerable.rb
blob9fb2f34e2c62146d02f9ebb9a139c9df2bba6db2
1 # depends on: module.rb class.rb
3 ##
4 #  The Enumerable mixin provides collection classes with  several traversal
5 #  and searching methods, and with the ability to sort. The class must provide
6 #  a method #each, which yields successive members of the collection. If
7 #  Enumerable#max, #min, or #sort is used, the objects in the collection must
8 #  also implement a meaningful <tt><=></tt> operator, as these methods rely on
9 #  an ordering between members of the collection.
11 module Enumerable
13   ##
14   #--
15   # Just to save you 10 seconds, the reason we always use #each to extract
16   # elements instead of something simpler is because Enumerable can not assume
17   # any other methods than #each. If needed, class-specific versions of any of
18   # these methods can be written *in those classes* to override these.
20   class Sort
22     def initialize(sorter = nil)
23       @sorter = sorter
24     end
26     def sort(xs, &prc)
27       # The ary should be inmutable while sorting
28       prc = Proc.new { |a,b| a <=> b } unless block_given?
30       if @sorter
31         @sorter = method(@sorter) unless @sorter.respond_to?(:call)
32         @sorter.call(xs, &prc)
33       else
34         quicksort(xs, &prc)
35       end
36     end
38     alias_method :call, :sort
40     class SortedElement
41       def initialize(val, sort_id)
42         @value, @sort_id = val, sort_id
43       end
45       attr_reader :value
46       attr_reader :sort_id
48       def <=>(other)
49         @sort_id <=> other.sort_id
50       end
51     end
53     def sort_by(xs)
54       # The ary and its elements sould be inmutable while sorting
56       elements = xs.map { |x| SortedElement.new(x, yield(x)) }
57       sort(elements).map { |e| e.value }
58     end
60     ##
61     # Sort an Enumerable using simple quicksort (not optimized)
63     def quicksort(xs, &prc)
64       return [] unless xs
66       pivot = Undefined
67       xs.each { |o| pivot = o; break }
68       return xs if pivot.equal? Undefined
70       lmr = xs.group_by do |o|
71         if o.equal?(pivot)
72           0
73         else
74           yield(o, pivot)
75         end
76       end
78       quicksort(lmr[-1], &prc) + lmr[0] + quicksort(lmr[1], &prc)
79     end
81   end
83   ##
84   # :call-seq:
85   #   enum.to_a      =>    array
86   #   enum.entries   =>    array
87   #
88   # Returns an array containing the items in +enum+.
89   #
90   #   (1..7).to_a                       #=> [1, 2, 3, 4, 5, 6, 7]
91   #   { 'a'=>1, 'b'=>2, 'c'=>3 }.to_a   #=> [["a", 1], ["b", 2], ["c", 3]]
93   def to_a
94     collect { |e| e }
95   end
97   alias_method :entries, :to_a
99   ##
100   # :call-seq:
101   #   enum.grep(pattern)                   => array
102   #   enum.grep(pattern) { | obj | block } => array
103   #
104   # Returns an array of every element in +enum+ for which <tt>Pattern ===
105   # element</tt>. If the optional +block+ is supplied, each matching element
106   # is passed to it, and the block's result is stored in the output array.
107   #
108   #   (1..100).grep 38..44   #=> [38, 39, 40, 41, 42, 43, 44]
109   #   c = IO.constants
110   #   c.grep(/SEEK/)         #=> ["SEEK_END", "SEEK_SET", "SEEK_CUR"]
111   #   res = c.grep(/SEEK/) { |v| IO.const_get(v) }
112   #   res                    #=> [2, 0, 1]
114   def grep(pattern)
115     ary = []
116     each do |o|
117       if pattern === o
118         ary << (block_given? ? yield(o) : o)
119       end
120     end
121     ary
122   end
124   def sorter
125     Enumerable::Sort.new
126   end
128   ##
129   # :call-seq:
130   #   enum.sort                     => array
131   #   enum.sort { | a, b | block }  => array
132   #
133   # Returns an array containing the items in +enum+ sorted, either according
134   # to their own <tt><=></tt> method, or by using the results of the supplied
135   # block. The block should return -1, 0, or +1 depending on the comparison
136   # between +a+> and +b+.  As of Ruby 1.8, the method Enumerable#sort_by
137   # implements a built-in Schwartzian Transform, useful when key computation
138   # or comparison is expensive..
139   #
140   #   %w(rhea kea flea).sort         #=> ["flea", "kea", "rhea"]
141   #   (1..10).sort { |a,b| b <=> a}  #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
143   def sort(&prc)
144     sorter.sort(self, &prc)
145   end
147   ##
148   # :call-seq:
149   #   enum.sort_by { | obj | block }    => array
150   #
151   # Sorts +enum+ using a set of keys generated by mapping the
152   # values in +enum+ through the given block.
153   #
154   #   %w{ apple pear fig }.sort_by { |word| word.length}
155   #   #=> ["fig", "pear", "apple"]
156   #
157   # The current implementation of sort_by generates an array of tuples
158   # containing the original collection element and the mapped value. This makes
159   # sort_by fairly expensive when the keysets are simple
160   #
161   #   require 'benchmark'
162   #   include Benchmark
163   #   
164   #   a = (1..100000).map {rand(100000)}
165   #   
166   #   bm(10) do |b|
167   #     b.report("Sort")    { a.sort }
168   #     b.report("Sort by") { a.sort_by { |a| a} }
169   #   end
170   #
171   # produces:
172   #
173   #   user     system      total        real
174   #   Sort        0.180000   0.000000   0.180000 (  0.175469)
175   #   Sort by     1.980000   0.040000   2.020000 (  2.013586)
176   #
177   # However, consider the case where comparing the keys is a non-trivial
178   # operation. The following code sorts some files on modification time
179   # using the basic sort method.
180   #
181   #   files = Dir["#"]
182   #   sorted = files.sort { |a,b| File.new(a).mtime <=> File.new(b).mtime}
183   #   sorted   #=> ["mon", "tues", "wed", "thurs"]
184   #
185   # This sort is inefficient: it generates two new File objects during every
186   # comparison. A slightly better technique is to use the Kernel#test method
187   # to generate the modification times directly.
188   #
189   #   files = Dir["#"]
190   #   sorted = files.sort { |a,b|
191   #     test(?M, a) <=> test(?M, b)
192   #   }
193   #   sorted   #=> ["mon", "tues", "wed", "thurs"]
194   #
195   # This still generates many unnecessary Time objects. A more efficient
196   # technique is to cache the sort keys (modification times in this case)
197   # before the sort. Perl users often call this approach a Schwartzian
198   # Transform, after Randal Schwartz. We construct a temporary array, where
199   # each element is an array containing our sort key along with the filename.
200   # We sort this array, and then extract the filename from the result.
201   #
202   #   sorted = Dir["#"].collect { |f|
203   #      [test(?M, f), f]
204   #   }.sort.collect { |f| f[1] }
205   #   sorted   #=> ["mon", "tues", "wed", "thurs"]
206   #
207   # This is exactly what sort_by does internally.
208   #
209   #   sorted = Dir["#"].sort_by { |f| test(?M, f)}
210   #   sorted   #=> ["mon", "tues", "wed", "thurs"]
212   def sort_by(&prc)
213     sorter.sort_by(self, &prc)
214   end
216   ##
217   # :call-seq:
218   #   enum.count(item)             => int
219   #   enum.count { | obj | block } => int
220   #
221   # Returns the number of items in +enum+ for which equals to +item+. If a
222   # block is given, counts the number of elements yielding a true value.
223   #
224   #   ary = [1, 2, 4, 2]
225   #   ary.count(2)          # => 2
226   #   ary.count{ |x|x%2==0}  # => 3
228   def count(item = Undefined)
229     seq = 0
230     unless item.equal? Undefined
231       each { |o| seq += 1 if item == o }
232     else
233       each { |o| seq += 1 if yield(o) }
234     end
235     seq
236   end
238   ##
239   # :call-seq:
240   #   enum.detect(ifnone = nil) { | obj | block }  => obj or nil
241   #   enum.find(ifnone = nil)   { | obj | block }  => obj or nil
242   #
243   # Passes each entry in +enum+ to +block+>. Returns the first for which
244   # +block+ is not false.  If no object matches, calls +ifnone+ and returns
245   # its result when it is specified, or returns nil
246   #
247   #   (1..10).detect  { |i| i % 5 == 0 and i % 7 == 0 }   #=> nil
248   #   (1..100).detect { |i| i % 5 == 0 and i % 7 == 0 }   #=> 35
250   def find(ifnone = nil)
251     each { |o| return o if yield(o) }
252     ifnone.call if ifnone
253   end
255   alias_method :detect, :find
257   ##
258   # :call-seq:
259   #   enum.find_index(ifnone = nil)   { | obj | block }  => int
260   #
261   # Passes each entry in +enum+ to +block+. Returns the index for the first
262   # for which +block+ is not false. If no object matches, returns
263   # nil.
264   #
265   #   (1..10).find_index  { |i| i % 5 == 0 and i % 7 == 0 }   #=> nil
266   #   (1..100).find_index { |i| i % 5 == 0 and i % 7 == 0 }   #=> 35
268   def find_index(ifnone = nil)
269     idx = -1
270     each { |o| idx += 1; return idx if yield(o) }
271     ifnone.call if ifnone
272   end
274   ##
275   # :call-seq:
276   #   enum.find_all { | obj | block }  => array
277   #   enum.select   { | obj | block }  => array
278   #
279   # Returns an array containing all elements of +enum+ for which +block+ is
280   # not false (see also Enumerable#reject).
281   #
282   #   (1..10).find_all { |i|  i % 3 == 0 }   #=> [3, 6, 9]
284   def find_all
285     ary = []
286     each do |o|
287       if yield(o)
288         ary << o
289       end
290     end
291     ary
292   end
294   alias_method :select, :find_all
296   ##
297   # :call-seq:
298   #   enum.reject { | obj | block }  => array
299   #
300   # Returns an array for all elements of +enum+ for which +block+ is false
301   # (see also Enumerable#find_all).
302   #
303   #    (1..10).reject { |i|  i % 3 == 0 }   #=> [1, 2, 4, 5, 7, 8, 10]
305   def reject
306     ary = []
307     each do |o|
308       unless yield(o)
309         ary << o
310       end
311     end
312     ary
313   end
315   ##
316   # :call-seq:
317   #   enum.collect { | obj | block }  => array
318   #   enum.map     { | obj | block }  => array
319   #
320   # Returns a new array with the results of running +block+ once for every
321   # element in +enum+.
322   #
323   #   (1..4).collect { |i| i*i }   #=> [1, 4, 9, 16]
324   #   (1..4).collect { "cat"  }   #=> ["cat", "cat", "cat", "cat"]
326   def collect
327     ary = []
328     if block_given?
329       each { |o| ary << yield(o) }
330     else
331       each { |o| ary << o }
332     end
333     ary
334   end
336   alias_method :map, :collect
338   ##
339   # :call-seq:
340   #   enum.inject(initial) { | memo, obj | block }  => obj
341   #   enum.inject          { | memo, obj | block }  => obj
342   #
343   # Combines the elements of +enum+ by applying the block to an accumulator
344   # value (+memo+) and each element in turn. At each step, +memo+ is set
345   # to the value returned by the block. The first form lets you supply an
346   # initial value for +memo+. The second form uses the first element of the
347   # collection as a the initial value (and skips that element while
348   # iterating).
349   #
350   # Sum some numbers:
351   #
352   #   (5..10).inject { |sum, n| sum + n }              #=> 45
353   #
354   # Multiply some numbers:
355   #
356   #   (5..10).inject(1) { |product, n| product * n }   #=> 151200
357   #
358   # Find the longest word:
359   #
360   #   longest = %w[ cat sheep bear ].inject do |memo,word|
361   #      memo.length > word.length ? memo : word
362   #   end
363   #   
364   #   longest                                         #=> "sheep"
365   #
366   # Find the length of the longest word:
367   #
368   #   longest = %w[ cat sheep bear ].inject(0) do |memo,word|
369   #      memo >= word.length ? memo : word.length
370   #   end
371   #   
372   #   longest                                         #=> 5
374   def inject(memo = Undefined)
375     each { |o|
376       if memo.equal? Undefined
377         memo = o
378       else
379         memo = yield(memo, o)
380       end
381     }
383     memo.equal?(Undefined) ? nil : memo
384   end
386   ##
387   # :call-seq:
388   #   enum.partition { | obj | block }  => [ true_array, false_array ]
389   #
390   # Returns two arrays, the first containing the elements of +enum+ for which
391   # the block evaluates to true, the second containing the rest.
392   #
393   #   (1..6).partition { |i| (i&1).zero?}   #=> [[2, 4, 6], [1, 3, 5]]
395   def partition
396     left = []
397     right = []
398     each { |o| yield(o) ? left.push(o) : right.push(o) }
399     return [left, right]
400   end
402   ##
403   # :call-seq:
404   #   enum.group_by { | obj | block }  => a_hash
405   #
406   # Returns a hash, which keys are evaluated result from the block, and values
407   # are arrays of elements in +enum+ corresponding to the key.
408   #
409   #    (1..6).group_by { |i| i%3}   #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
411   def group_by
412     h = {}
413     each do |o|
414       key = yield(o)
415       if h.key?(key)
416         h[key] << o
417       else
418         h[key] = [o]
419       end
420     end
421     h
422   end
424   ##
425   # :call-seq:
426   #   enum.first      => obj or nil
427   #   enum.first(n)   => an_array
428   #
429   # Returns the first element, or the first +n+ elements, of the enumerable.
430   # If the enumerable is empty, the first form returns nil, and the second
431   # form returns an empty array.
433   def first(n = nil)
434     if n && n < 0
435       raise ArgumentError, "Invalid number of elements given."
436     end
437     ary = []
438     each do |o|
439       return o unless n
440       return ary if ary.size == n
441       ary << o
442     end
443     n ? ary : nil
444   end
446   # :call-seq:
447   #   enum.all?                     => true or false
448   #   enum.all? { |obj| block }   => true or false
449   #
450   # Passes each element of the collection to the given block. The method
451   # returns true if the block never returns false or nil. If the block is not
452   # given, Ruby adds an implicit block of <tt>{ |obj| obj }</tt> (that is all?
453   # will return true only if none of the collection members are
454   # false or nil.)
455   #
456   #   %w[ant bear cat].all? { |word| word.length >= 3}   #=> true
457   #   %w[ant bear cat].all? { |word| word.length >= 4}   #=> false
458   #   [ nil, true, 99 ].all?                             #=> false
460   def all?(&prc)
461     prc = Proc.new { |obj| obj } unless block_given?
462     each { |o| return false unless prc.call(o) }
463     true
464   end
466   ##
467   # :call-seq:
468   #    enum.any? [{ |obj| block } ]   => true or false
469   #
470   # Passes each element of the collection to the given block. The method
471   # returns true if the block ever returns a value other than false or nil. If
472   # the block is not given, Ruby adds an implicit block of <tt>{ |obj| obj
473   # }</tt> (that is any? will return true if at least one of the collection
474   # members is not false or nil.
475   #
476   #   %w[ant bear cat].any? { |word| word.length >= 3}   #=> true
477   #   %w[ant bear cat].any? { |word| word.length >= 4}   #=> true
478   #   [ nil, true, 99 ].any?                             #=> true
480   def any?(&prc)
481     prc = Proc.new { |obj| obj } unless block_given?
482     each { |o| return true if prc.call(o) }
483     false
484   end
486   ##
487   # :call-seq:
488   #   enum.one?                   => true or false
489   #   enum.one? { |obj| block }   => true or false
490   #
491   # Passes each element of the collection to the given block. The method
492   # returns true if the block returns true exactly once. If the block is not
493   # given, one? will return true only if exactly one of the collection members
494   # are true.
495   #
496   #   %w[ant bear cat].one? { |word| word.length == 4}   #=> true
497   #   %w[ant bear cat].one? { |word| word.length >= 4}   #=> false
498   #   [ nil, true, 99 ].one?                             #=> true
500   def one?(&prc)
501     prc = Proc.new { |obj| obj } unless block_given?
502     times = 0
503     each { |o| times += 1 if prc.call(o) }
504     times == 1
505   end
507   ##
508   # :call-seq:
509   #   enum.none?                   => true or false
510   #   enum.none? { |obj| block }   => true or false
511   #
512   # Passes each element of the collection to the given block. The method
513   # returns true if the block never returns true for all elements. If the
514   # block is not given, none? will return true only if any of the collection
515   # members is true.
516   #
517   #    %w{ant bear cat}.none? { |word| word.length == 4}   #=> true
518   #    %w{ant bear cat}.none? { |word| word.length >= 4}   #=> false
519   #    [ nil, true, 99 ].none?                             #=> true
521   def none?(&prc)
522     prc = Proc.new { |obj| obj } unless block_given?
523     times = 0
524     each { |o| times += 1 if prc.call(o) }
525     times == 0
526   end
528   ##
529   # :call-seq:
530   #   enum.min                    => obj
531   #   enum.min { | a,b | block }  => obj
532   #
533   # Returns the object in +enum+ with the minimum value. The first form
534   # assumes all objects implement Comparable; the second uses the block to
535   # return <tt>a <=> b</tt>.
536   #
537   #   a = %w[albatross dog horse]
538   #   a.min                                  #=> "albatross"
539   #   a.min { |a,b| a.length <=> b.length }   #=> "dog"
541   def min(&prc)
542     prc = Proc.new { |a, b| a <=> b } unless block_given?
543     min = Undefined
544     each do |o|
545       if min.equal? Undefined
546         min = o
547       else
548         comp = prc.call(o, min)
549         if comp.nil?
550           raise ArgumentError, "comparison of #{o.class} with #{min} failed"
551         elsif comp < 0
552           min = o
553         end
554       end
555     end
557     min.equal?(Undefined) ? nil : min
558   end
560   ##
561   # :call-seq:
562   #   enum.max                   => obj
563   #   enum.max { |a,b| block }   => obj
564   #
565   # Returns the object in +enum+ with the maximum value. The first form
566   # assumes all objects implement Comparable; the second uses the block to
567   # return <tt>a <=> b</tt>.
568   #
569   #    a = %w[albatross dog horse]
570   #    a.max                                  #=> "horse"
571   #    a.max { |a,b| a.length <=> b.length }   #=> "albatross"
573   def max(&prc)
574     prc = Proc.new { |a, b| a <=> b } unless block_given?
575     max = Undefined
576     each do |o|
577       if max.equal? Undefined
578         max = o
579       else
580         comp = prc.call(o, max)
581         if comp.nil?
582           raise ArgumentError, "comparison of #{o.class} with #{max} failed"
583         elsif comp > 0
584           max = o
585         end
586       end
587     end
589     max.equal?(Undefined) ? nil : max
590   end
592   ##
593   # :call-seq:
594   #   enum.min_by { |obj| block }   => obj
595   #
596   # Uses the values returned by the given block as a substitute for the real
597   # object to determine what is considered the smallest object in +enum+ using
598   # <tt>lhs <=> rhs</tt>. In the event of a tie, the object that appears first
599   # in #each is chosen. Returns the "smallest" object or nil if the enum is
600   # empty.
601   #
602   #   a = %w[albatross dog horse]
603   #   a.min_by { |x| x.length }   #=> "dog"
605   def min_by()
606     min_obj, min_value = Undefined, Undefined
608     each do |o|
609       value = yield(o)
611       if min_obj.equal?(Undefined) or (min_value <=> value) > 0
612         min_obj, min_value = o, value
613       end
614     end
616     min_obj.equal?(Undefined) ? nil : min_obj
617   end
619   ##
620   # :call-seq:
621   #   enum.max_by { | obj| block }   => obj
622   #
623   # Uses the values returned by the given block as a substitute for the real
624   # object to determine what is considered the largest object in +enum+ using
625   # <tt>lhs <=> rhs</tt>. In the event of a tie, the object that appears first
626   # in #each is chosen. Returns the "largest" object or nil if the enum is
627   # empty.
628   #
629   #   a = %w[albatross dog horse]
630   #   a.max_by { |x| x.length }   #=> "albatross"
632   def max_by()
633     max_obj, max_value = Undefined, Undefined
635     each do |o|
636       value = yield(o)
638       if max_obj.equal?(Undefined) or (max_value <=> value) < 0
639         max_obj, max_value = o, value
640       end
641     end
643     max_obj.equal?(Undefined) ? nil : max_obj
644   end
647   # :call-seq:
648   #   enum.include?(obj)     => true or false
649   #   enum.member?(obj)      => true or false
650   #
651   # Returns true if any member of +enum+ equals +obj+. Equality is tested
652   # using #==.
653   #
654   #   IO.constants.include? "SEEK_SET"          #=> true
655   #   IO.constants.include? "SEEK_NO_FURTHER"   #=> false
657   def include?(obj)
658     each { |o| return true if obj == o }
659     false
660   end
662   alias_method :member?, :include?
664   ##
665   # :call-seq:
666   #   enum.each_with_index { |obj, i| block }  -> enum
667   #
668   # Calls +block+ with two arguments, the item and its index, for
669   # each item in +enum+.
670   #
671   #   hash = {}
672   #   %w[cat dog wombat].each_with_index { |item, index|
673   #     hash[item] = index
674   #   }
675   #   
676   #   p hash   #=> {"cat"=>0, "wombat"=>2, "dog"=>1}
678   def each_with_index
679     idx = 0
680     each { |o| yield(o, idx); idx += 1 }
681     self
682   end
684   ##
685   # :call-seq:
686   #    enum.zip(arg, ...)                   => array
687   #    enum.zip(arg, ...) { |arr| block }   => nil
688   #
689   # Converts any arguments to arrays, then merges elements of +enum+ with
690   # corresponding elements from each argument. This generates a sequence of
691   # enum#size +n+-element arrays, where +n+ is one more that the count of
692   # arguments. If the size of any argument is less than enum#size, nil values
693   # are supplied. If a block given, it is invoked for each output array,
694   # otherwise an array of arrays is returned.
695   #
696   #   a = [ 4, 5, 6 ]
697   #   b = [ 7, 8, 9 ]
698   #
699   #   (1..3).zip(a, b)      #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
700   #   "cat\ndog".zip([1])   #=> [["cat\n", 1], ["dog", nil]]
701   #   (1..3).zip            #=> [[1], [2], [3]]
703   def zip(*args)
704     result = []
705     args = args.map { |a| a.to_a }
706     each_with_index do |o, i|
707       result << args.inject([o]) { |ary, a| ary << a[i] }
708       yield(result.last) if block_given?
709     end
710     result unless block_given?
711   end