Merge pull request #2162 from ksss/__send__
[mruby.git] / mrblib / enum.rb
blob41fd97fe7e0414d30a9fe2215216e7b6bf440845
1 ##
2 # Enumerable
4 #  ISO 15.3.2
6 #  The <code>Enumerable</code> mixin provides collection classes with
7 #  several traversal and searching methods, and with the ability to
8 #  sort. The class must provide a method <code>each</code>, which
9 #  yields successive members of the collection. If
10 #  <code>Enumerable#max</code>, <code>#min</code>, or
11 #  <code>#sort</code> is used, the objects in the collection must also
12 #  implement a meaningful <code><=></code> operator, as these methods
13 #  rely on an ordering between members of the collection.
15 module Enumerable
17   ##
18   # Call the given block for each element
19   # which is yield by +each+. Return false
20   # if one block value is false. Otherwise
21   # return true. If no block is given and
22   # +self+ is false return false.
23   #
24   # ISO 15.3.2.2.1
25   def all?(&block)
26     if block
27       self.each{|*val|
28         unless block.call(*val)
29           return false
30         end
31       }
32     else
33       self.each{|*val|
34         unless val.__svalue
35           return false
36         end
37       }
38     end
39     true
40   end
42   ##
43   # Call the given block for each element
44   # which is yield by +each+. Return true
45   # if one block value is true. Otherwise
46   # return false. If no block is given and
47   # +self+ is true object return true.
48   #
49   # ISO 15.3.2.2.2
50   def any?(&block)
51     if block
52       self.each{|*val|
53         if block.call(*val)
54           return true
55         end
56       }
57     else
58       self.each{|*val|
59         if val.__svalue
60           return true
61         end
62       }
63     end
64     false
65   end
67   ##
68   # Call the given block for each element
69   # which is yield by +each+. Append all
70   # values of each block together and
71   # return this value.
72   #
73   # ISO 15.3.2.2.3
74   def collect(&block)
75     return to_enum :collect unless block_given?
77     ary = []
78     self.each{|*val|
79       ary.push(block.call(*val))
80     }
81     ary
82   end
84   ##
85   # Call the given block for each element
86   # which is yield by +each+. Return
87   # +ifnone+ if no block value was true.
88   # Otherwise return the first block value
89   # which had was true.
90   #
91   # ISO 15.3.2.2.4
92   def detect(ifnone=nil, &block)
93     ret = ifnone
94     self.each{|*val|
95       if block.call(*val)
96         ret = val.__svalue
97         break
98       end
99     }
100     ret
101   end
103   ##
104   # Call the given block for each element
105   # which is yield by +each+. Pass an
106   # index to the block which starts at 0
107   # and increase by 1 for each element.
108   #
109   # ISO 15.3.2.2.5
110   def each_with_index(&block)
111     return to_enum :each_with_index unless block_given?
113     i = 0
114     self.each{|*val|
115       block.call(val.__svalue, i)
116       i += 1
117     }
118     self
119   end
121   ##
122   # Return an array of all elements which
123   # are yield by +each+.
124   #
125   # ISO 15.3.2.2.6
126   def entries
127     ary = []
128     self.each{|*val|
129       # __svalue is an internal method
130       ary.push val.__svalue
131     }
132     ary
133   end
135   ##
136   # Alias for find
137   #
138   # ISO 15.3.2.2.7
139   alias find detect
141   ##
142   # Call the given block for each element
143   # which is yield by +each+. Return an array
144   # which contains all elements whose block
145   # value was true.
146   #
147   # ISO 15.3.2.2.8
148   def find_all(&block)
149     return to_enum :find_all unless block_given?
151     ary = []
152     self.each{|*val|
153       ary.push(val.__svalue) if block.call(*val)
154     }
155     ary
156   end
158   ##
159   # Call the given block for each element
160   # which is yield by +each+ and which return
161   # value was true when invoking === with
162   # +pattern+. Return an array with all
163   # elements or the respective block values.
164   #
165   # ISO 15.3.2.2.9
166   def grep(pattern, &block)
167     ary = []
168     self.each{|*val|
169       sv = val.__svalue
170       if pattern === sv
171         ary.push((block)? block.call(*val): sv)
172       end
173     }
174     ary
175   end
177   ##
178   # Return true if at least one element which
179   # is yield by +each+ returns a true value
180   # by invoking == with +obj+. Otherwise return
181   # false.
182   #
183   # ISO 15.3.2.2.10
184   def include?(obj)
185     self.each{|*val|
186       if val.__svalue == obj
187         return true
188       end
189     }
190     false
191   end
193   ##
194   # Call the given block for each element
195   # which is yield by +each+. Return value
196   # is the sum of all block values. Pass
197   # to each block the current sum and the
198   # current element.
199   #
200   # ISO 15.3.2.2.11
201   def inject(*args, &block)
202     raise ArgumentError, "too many arguments" if args.size > 2
203     if Symbol === args[-1]
204       sym = args[-1]
205       block = ->(x,y){x.__send__(sym,y)}
206       args.pop
207     end
208     if args.empty?
209       flag = true  # no initial argument
210       result = nil
211     else
212       flag = false
213       result = args[0]
214     end
215     self.each{|*val|
216       val = val.__svalue
217       if flag
218         # push first element as initial
219         flag = false
220         result = val
221       else
222         result = block.call(result, val)
223       end
224     }
225     result
226   end
227   alias reduce inject
229   ##
230   # Alias for collect
231   #
232   # ISO 15.3.2.2.12
233   alias map collect
235   ##
236   # Return the maximum value of all elements
237   # yield by +each+. If no block is given <=>
238   # will be invoked to define this value. If
239   # a block is given it will be used instead.
240   #
241   # ISO 15.3.2.2.13
242   def max(&block)
243     flag = true  # 1st element?
244     result = nil
245     self.each{|*val|
246       val = val.__svalue
247       if flag
248         # 1st element
249         result = val
250         flag = false
251       else
252         if block
253           result = val if block.call(val, result) > 0
254         else
255           result = val if (val <=> result) > 0
256         end
257       end
258     }
259     result
260   end
262   ##
263   # Return the minimum value of all elements
264   # yield by +each+. If no block is given <=>
265   # will be invoked to define this value. If
266   # a block is given it will be used instead.
267   #
268   # ISO 15.3.2.2.14
269   def min(&block)
270     flag = true  # 1st element?
271     result = nil
272     self.each{|*val|
273       val = val.__svalue
274       if flag
275         # 1st element
276         result = val
277         flag = false
278       else
279         if block
280           result = val if block.call(val, result) < 0
281         else
282           result = val if (val <=> result) < 0
283         end
284       end
285     }
286     result
287   end
289   ##
290   # Alias for include?
291   #
292   # ISO 15.3.2.2.15
293   alias member? include?
295   ##
296   # Call the given block for each element
297   # which is yield by +each+. Return an
298   # array which contains two arrays. The
299   # first array contains all elements
300   # whose block value was true. The second
301   # array contains all elements whose
302   # block value was false.
303   #
304   # ISO 15.3.2.2.16
305   def partition(&block)
306     ary_T = []
307     ary_F = []
308     self.each{|*val|
309       if block.call(*val)
310         ary_T.push(val.__svalue)
311       else
312         ary_F.push(val.__svalue)
313       end
314     }
315     [ary_T, ary_F]
316   end
318   ##
319   # Call the given block for each element
320   # which is yield by +each+. Return an
321   # array which contains only the elements
322   # whose block value was false.
323   #
324   # ISO 15.3.2.2.17
325   def reject(&block)
326     ary = []
327     self.each{|*val|
328       ary.push(val.__svalue) unless block.call(*val)
329     }
330     ary
331   end
333   ##
334   # Alias for find_all.
335   #
336   # ISO 15.3.2.2.18
337   alias select find_all
339   ##
340   # TODO
341   # Does this OK? Please test it.
342   def __sort_sub__(sorted, work, src_ary, head, tail, &block)
343     if head == tail
344       sorted[head] = work[head] if src_ary == 1
345       return
346     end
348     # on current step, which is a src ary?
349     if src_ary == 0
350       src, dst = sorted, work
351     else
352       src, dst = work, sorted
353     end
355     key = src[head]    # key value for dividing values
356     i, j = head, tail  # position to store on the dst ary
358     (head + 1).upto(tail){|idx|
359       if ((block)? block.call(src[idx], key): (src[idx] <=> key)) > 0
360         # larger than key
361         dst[j] = src[idx]
362         j -= 1
363       else
364         dst[i] = src[idx]
365         i += 1
366       end
367     }
369     sorted[i] = key
371     # sort each sub-array
372     src_ary = (src_ary + 1) % 2  # exchange a src ary
373     __sort_sub__(sorted, work, src_ary, head, i - 1, &block) if i > head
374     __sort_sub__(sorted, work, src_ary, i + 1, tail, &block) if i < tail
375   end
376 #  private :__sort_sub__
378   ##
379   # Return a sorted array of all elements
380   # which are yield by +each+. If no block
381   # is given <=> will be invoked on each
382   # element to define the order. Otherwise
383   # the given block will be used for
384   # sorting.
385   #
386   # ISO 15.3.2.2.19
387   def sort(&block)
388     ary = []
389     self.each{|*val| ary.push(val.__svalue)}
390     if ary.size > 1
391       __sort_sub__(ary, ::Array.new(ary.size), 0, 0, ary.size - 1, &block)
392     end
393     ary
394   end
396   ##
397   # Alias for entries.
398   #
399   # ISO 15.3.2.2.20
400   alias to_a entries
402   # redefine #hash 15.3.1.3.15
403   def hash
404     h = 12347
405     self.each do |e|
406       h ^= e.hash
407     end
408     h
409   end