Merge pull request #6553 from mruby/add-vm-comments
[mruby.git] / mrblib / enum.rb
index e6aa682..23b275a 100644 (file)
@@ -1,19 +1,20 @@
 ##
 # Enumerable
 #
-#  ISO 15.3.2
+# The <code>Enumerable</code> mixin provides collection classes with
+# several traversal and searching methods, and with the ability to
+# sort. The class must provide a method `each`, which
+# yields successive members of the collection. If
+# {Enumerable#max}, {#min}, or
+# {#sort} is used, the objects in the collection must also
+# implement a meaningful `<=>` operator, as these methods
+# rely on an ordering between members of the collection.
 #
-#  The <code>Enumerable</code> mixin provides collection classes with
-#  several traversal and searching methods, and with the ability to
-#  sort. The class must provide a method <code>each</code>, which
-#  yields successive members of the collection. If
-#  <code>Enumerable#max</code>, <code>#min</code>, or
-#  <code>#sort</code> is used, the objects in the collection must also
-#  implement a meaningful <code><=></code> operator, as these methods
-#  rely on an ordering between members of the collection.
-
+# ISO 15.3.2
 module Enumerable
 
+  NONE = Object.new
+
   ##
   # Call the given block for each element
   # which is yield by +each+. Return false
@@ -23,23 +24,12 @@ module Enumerable
   #
   # ISO 15.3.2.2.1
   def all?(&block)
-    st = true
     if block
-      self.each{|val|
-        unless block.call(val)
-          st = false
-          break
-        end
-      }
+      self.each{|*val| return false unless block.call(*val)}
     else
-      self.each{|val|
-        unless val
-          st = false
-          break
-        end
-      }
+      self.each{|*val| return false unless val.__svalue}
     end
-    st
+    true
   end
 
   ##
@@ -51,23 +41,12 @@ module Enumerable
   #
   # ISO 15.3.2.2.2
   def any?(&block)
-    st = false
     if block
-      self.each{|val|
-        if block.call(val)
-          st = true
-          break
-        end
-      }
+      self.each{|*val| return true if block.call(*val)}
     else
-      self.each{|val|
-        if val
-          st = true
-          break
-        end
-      }
+      self.each{|*val| return true if val.__svalue}
     end
-    st
+    false
   end
 
   ##
@@ -78,30 +57,30 @@ module Enumerable
   #
   # ISO 15.3.2.2.3
   def collect(&block)
+    return to_enum :collect unless block
+
     ary = []
-    self.each{|val|
-      ary.push(block.call(val))
-    }
+    self.each{|*val| ary.push(block.call(*val))}
     ary
   end
 
   ##
-  # Call the given block for each element
-  # which is yield by +each+. Return
-  # +ifnone+ if no block value was true.
-  # Otherwise return the first block value
-  # which had was true.
+  # Return the first element for which
+  # value from the block is true. If no
+  # object matches, calls +ifnone+ and
+  # returns its result. Otherwise returns
+  # +nil+.
   #
   # ISO 15.3.2.2.4
   def detect(ifnone=nil, &block)
-    ret = ifnone
-    self.each{|val|
-      if block.call(val)
-        ret = val
-        break
+    return to_enum :detect, ifnone unless block
+
+    self.each{|*val|
+      if block.call(*val)
+        return val.__svalue
       end
     }
-    ret
+    ifnone.call unless ifnone.nil?
   end
 
   ##
@@ -112,9 +91,11 @@ module Enumerable
   #
   # ISO 15.3.2.2.5
   def each_with_index(&block)
+    return to_enum :each_with_index unless block
+
     i = 0
-    self.each{|val|
-      block.call(val, i)
+    self.each{|*val|
+      block.call(val.__svalue, i)
       i += 1
     }
     self
@@ -127,8 +108,9 @@ module Enumerable
   # ISO 15.3.2.2.6
   def entries
     ary = []
-    self.each{|val|
-      ary.push val
+    self.each{|*val|
+      # __svalue is an internal method
+      ary.push val.__svalue
     }
     ary
   end
@@ -147,9 +129,11 @@ module Enumerable
   #
   # ISO 15.3.2.2.8
   def find_all(&block)
+    return to_enum :find_all unless block
+
     ary = []
-    self.each{|val|
-      ary.push(val) if block.call(val)
+    self.each{|*val|
+      ary.push(val.__svalue) if block.call(*val)
     }
     ary
   end
@@ -164,9 +148,10 @@ module Enumerable
   # ISO 15.3.2.2.9
   def grep(pattern, &block)
     ary = []
-    self.each{|val|
-      if pattern === val
-        ary.push((block)? block.call(val): val)
+    self.each{|*val|
+      sv = val.__svalue
+      if pattern === sv
+        ary.push((block)? block.call(*val): sv)
       end
     }
     ary
@@ -180,14 +165,10 @@ module Enumerable
   #
   # ISO 15.3.2.2.10
   def include?(obj)
-    st = false
-    self.each{|val|
-      if val == obj
-        st = true
-        break
-      end
+    self.each{|*val|
+      return true if val.__svalue == obj
     }
-    st
+    false
   end
 
   ##
@@ -202,7 +183,7 @@ module Enumerable
     raise ArgumentError, "too many arguments" if args.size > 2
     if Symbol === args[-1]
       sym = args[-1]
-      block = ->(x,y){x.send(sym,y)}
+      block = ->(x,y){x.__send__(sym,y)}
       args.pop
     end
     if args.empty?
@@ -212,7 +193,8 @@ module Enumerable
       flag = false
       result = args[0]
     end
-    self.each{|val|
+    self.each{|*val|
+      val = val.__svalue
       if flag
         # push first element as initial
         flag = false
@@ -241,7 +223,8 @@ module Enumerable
   def max(&block)
     flag = true  # 1st element?
     result = nil
-    self.each{|val|
+    self.each{|*val|
+      val = val.__svalue
       if flag
         # 1st element
         result = val
@@ -267,7 +250,8 @@ module Enumerable
   def min(&block)
     flag = true  # 1st element?
     result = nil
-    self.each{|val|
+    self.each{|*val|
+      val = val.__svalue
       if flag
         # 1st element
         result = val
@@ -300,13 +284,15 @@ module Enumerable
   #
   # ISO 15.3.2.2.16
   def partition(&block)
+    return to_enum :partition unless block
+
     ary_T = []
     ary_F = []
-    self.each{|val|
-      if block.call(val)
-        ary_T.push(val)
+    self.each{|*val|
+      if block.call(*val)
+        ary_T.push(val.__svalue)
       else
-        ary_F.push(val)
+        ary_F.push(val.__svalue)
       end
     }
     [ary_T, ary_F]
@@ -320,9 +306,11 @@ module Enumerable
   #
   # ISO 15.3.2.2.17
   def reject(&block)
+    return to_enum :reject unless block
+
     ary = []
-    self.each{|val|
-      ary.push(val) unless block.call(val)
+    self.each{|*val|
+      ary.push(val.__svalue) unless block.call(*val)
     }
     ary
   end
@@ -334,45 +322,6 @@ module Enumerable
   alias select find_all
 
   ##
-  # TODO
-  # Does this OK? Please test it.
-  def __sort_sub__(sorted, work, src_ary, head, tail, &block)
-    if head == tail
-      sorted[head] = work[head] if src_ary == 1
-      return
-    end
-
-    # on current step, which is a src ary?
-    if src_ary == 0
-      src, dst = sorted, work
-    else
-      src, dst = work, sorted
-    end
-
-    key = src[head]    # key value for dividing values
-    i, j = head, tail  # position to store on the dst ary
-
-    (head + 1).upto(tail){|idx|
-      if ((block)? block.call(src[idx], key): (src[idx] <=> key)) > 0
-        # larger than key
-        dst[j] = src[idx]
-        j -= 1
-      else
-        dst[i] = src[idx]
-        i += 1
-      end
-    }
-
-    sorted[i] = key
-
-    # sort each sub-array
-    src_ary = (src_ary + 1) % 2  # exchange a src ary
-    __sort_sub__(sorted, work, src_ary, head, i - 1, &block) if i > head
-    __sort_sub__(sorted, work, src_ary, i + 1, tail, &block) if i < tail
-  end
-#  private :__sort_sub__
-
-  ##
   # Return a sorted array of all elements
   # which are yield by +each+. If no block
   # is given <=> will be invoked on each
@@ -382,12 +331,7 @@ module Enumerable
   #
   # ISO 15.3.2.2.19
   def sort(&block)
-    ary = []
-    self.each{|val| ary.push(val)}
-    unless ary.empty?
-      __sort_sub__(ary, ::Array.new(ary.size), 0, 0, ary.size - 1, &block)
-    end
-    ary
+    self.map{|*val| val.__svalue}.sort(&block)
   end
 
   ##
@@ -399,8 +343,10 @@ module Enumerable
   # redefine #hash 15.3.1.3.15
   def hash
     h = 12347
+    i = 0
     self.each do |e|
-      h ^= e.hash
+      h = __update_hash(h, i, e.hash)
+      i += 1
     end
     h
   end