source: EcnlProtoTool/trunk/mruby-1.2.0/mrbgems/mruby-enumerator/mrblib/enumerator.rb@ 321

Last change on this file since 321 was 321, checked in by coas-nagasima, 7 years ago

文字コードを設定

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-ruby;charset=UTF-8
File size: 15.6 KB
Line 
1##
2# enumerator.rb Enumerator class
3# See Copyright Notice in mruby.h
4
5##
6# A class which allows both internal and external iteration.
7#
8# An Enumerator can be created by the following methods.
9# - {Kernel#to_enum}
10# - {Kernel#enum_for}
11# - {Enumerator#initialize Enumerator.new}
12#
13# Most methods have two forms: a block form where the contents
14# are evaluated for each item in the enumeration, and a non-block form
15# which returns a new Enumerator wrapping the iteration.
16#
17# enumerator = %w(one two three).each
18# puts enumerator.class # => Enumerator
19#
20# enumerator.each_with_object("foo") do |item, obj|
21# puts "#{obj}: #{item}"
22# end
23#
24# # foo: one
25# # foo: two
26# # foo: three
27#
28# enum_with_obj = enumerator.each_with_object("foo")
29# puts enum_with_obj.class # => Enumerator
30#
31# enum_with_obj.each do |item, obj|
32# puts "#{obj}: #{item}"
33# end
34#
35# # foo: one
36# # foo: two
37# # foo: three
38#
39# This allows you to chain Enumerators together. For example, you
40# can map a list's elements to strings containing the index
41# and the element as a string via:
42#
43# puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" }
44# # => ["0:foo", "1:bar", "2:baz"]
45#
46# An Enumerator can also be used as an external iterator.
47# For example, Enumerator#next returns the next value of the iterator
48# or raises StopIteration if the Enumerator is at the end.
49#
50# e = [1,2,3].each # returns an enumerator object.
51# puts e.next # => 1
52# puts e.next # => 2
53# puts e.next # => 3
54# puts e.next # raises StopIteration
55#
56# You can use this to implement an internal iterator as follows:
57#
58# def ext_each(e)
59# while true
60# begin
61# vs = e.next_values
62# rescue StopIteration
63# return $!.result
64# end
65# y = yield(*vs)
66# e.feed y
67# end
68# end
69#
70# o = Object.new
71#
72# def o.each
73# puts yield
74# puts yield(1)
75# puts yield(1, 2)
76# 3
77# end
78#
79# # use o.each as an internal iterator directly.
80# puts o.each {|*x| puts x; [:b, *x] }
81# # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
82#
83# # convert o.each to an external iterator for
84# # implementing an internal iterator.
85# puts ext_each(o.to_enum) {|*x| puts x; [:b, *x] }
86# # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
87#
88class Enumerator
89 include Enumerable
90
91 ##
92 # @overload initialize(size = nil, &block)
93 # @overload initialize(obj, method = :each, *args)
94 #
95 # Creates a new Enumerator object, which can be used as an
96 # Enumerable.
97 #
98 # In the first form, iteration is defined by the given block, in
99 # which a "yielder" object, given as block parameter, can be used to
100 # yield a value by calling the +yield+ method (aliased as +<<+):
101 #
102 # fib = Enumerator.new do |y|
103 # a = b = 1
104 # loop do
105 # y << a
106 # a, b = b, a + b
107 # end
108 # end
109 #
110 # p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
111 #
112 def initialize(obj=nil, meth=:each, *args, &block)
113 if block_given?
114 obj = Generator.new(&block)
115 else
116 raise ArgumentError unless obj
117 end
118
119 @obj = obj
120 @meth = meth
121 @args = args.dup
122 @fib = nil
123 @dst = nil
124 @lookahead = nil
125 @feedvalue = nil
126 @stop_exc = false
127 end
128 attr_accessor :obj, :meth, :args, :fib
129 private :obj, :meth, :args, :fib
130
131 def initialize_copy(obj)
132 raise TypeError, "can't copy type #{obj.class}" unless obj.kind_of? Enumerator
133 raise TypeError, "can't copy execution context" if obj.fib
134 @obj = obj.obj
135 @meth = obj.meth
136 @args = obj.args
137 @fib = nil
138 @lookahead = nil
139 @feedvalue = nil
140 self
141 end
142
143 ##
144 # call-seq:
145 # e.with_index(offset = 0) {|(*args), idx| ... }
146 # e.with_index(offset = 0)
147 #
148 # Iterates the given block for each element with an index, which
149 # starts from +offset+. If no block is given, returns a new Enumerator
150 # that includes the index, starting from +offset+
151 #
152 # +offset+:: the starting index to use
153 #
154 def with_index(offset=0)
155 return to_enum :with_index, offset unless block_given?
156 raise TypeError, "no implicit conversion of #{offset.class} into Integer" unless offset.respond_to?(:to_int)
157
158 n = offset.to_int - 1
159 enumerator_block_call do |i|
160 n += 1
161 yield [i,n]
162 end
163 end
164
165 ##
166 # call-seq:
167 # e.each_with_index {|(*args), idx| ... }
168 # e.each_with_index
169 #
170 # Same as Enumerator#with_index(0), i.e. there is no starting offset.
171 #
172 # If no block is given, a new Enumerator is returned that includes the index.
173 #
174 def each_with_index
175 with_index
176 end
177
178 ##
179 # call-seq:
180 # e.each_with_object(obj) {|(*args), obj| ... }
181 # e.each_with_object(obj)
182 # e.with_object(obj) {|(*args), obj| ... }
183 # e.with_object(obj)
184 #
185 # Iterates the given block for each element with an arbitrary object, +obj+,
186 # and returns +obj+
187 #
188 # If no block is given, returns a new Enumerator.
189 #
190 # @example
191 # to_three = Enumerator.new do |y|
192 # 3.times do |x|
193 # y << x
194 # end
195 # end
196 #
197 # to_three_with_string = to_three.with_object("foo")
198 # to_three_with_string.each do |x,string|
199 # puts "#{string}: #{x}"
200 # end
201 #
202 # # => foo:0
203 # # => foo:1
204 # # => foo:2
205 #
206 def with_object(object)
207 return to_enum(:with_object, object) unless block_given?
208
209 enumerator_block_call do |i|
210 yield [i,object]
211 end
212 object
213 end
214
215 def inspect
216 return "#<#{self.class}: uninitialized>" unless @obj
217
218 if @args && @args.size > 0
219 args = @args.join(", ")
220 "#<#{self.class}: #{@obj}:#{@meth}(#{args})>"
221 else
222 "#<#{self.class}: #{@obj}:#{@meth}>"
223 end
224 end
225
226 ##
227 # call-seq:
228 # enum.each { |elm| block } -> obj
229 # enum.each -> enum
230 # enum.each(*appending_args) { |elm| block } -> obj
231 # enum.each(*appending_args) -> an_enumerator
232 #
233 # Iterates over the block according to how this Enumerator was constructed.
234 # If no block and no arguments are given, returns self.
235 #
236 # === Examples
237 #
238 # "Hello, world!".scan(/\w+/) #=> ["Hello", "world"]
239 # "Hello, world!".to_enum(:scan, /\w+/).to_a #=> ["Hello", "world"]
240 # "Hello, world!".to_enum(:scan).each(/\w+/).to_a #=> ["Hello", "world"]
241 #
242 # obj = Object.new
243 #
244 # def obj.each_arg(a, b=:b, *rest)
245 # yield a
246 # yield b
247 # yield rest
248 # :method_returned
249 # end
250 #
251 # enum = obj.to_enum :each_arg, :a, :x
252 #
253 # enum.each.to_a #=> [:a, :x, []]
254 # enum.each.equal?(enum) #=> true
255 # enum.each { |elm| elm } #=> :method_returned
256 #
257 # enum.each(:y, :z).to_a #=> [:a, :x, [:y, :z]]
258 # enum.each(:y, :z).equal?(enum) #=> false
259 # enum.each(:y, :z) { |elm| elm } #=> :method_returned
260 #
261 def each(*argv, &block)
262 obj = self
263 if 0 < argv.length
264 obj = self.dup
265 args = obj.args
266 if !args.empty?
267 args = args.dup
268 args.concat argv
269 else
270 args = argv.dup
271 end
272 obj.args = args
273 end
274 return obj unless block_given?
275 enumerator_block_call(&block)
276 end
277
278 def enumerator_block_call(&block)
279 @obj.__send__ @meth, *@args, &block
280 end
281 private :enumerator_block_call
282
283 ##
284 # call-seq:
285 # e.next -> object
286 #
287 # Returns the next object in the enumerator, and move the internal position
288 # forward. When the position reached at the end, StopIteration is raised.
289 #
290 # === Example
291 #
292 # a = [1,2,3]
293 # e = a.to_enum
294 # p e.next #=> 1
295 # p e.next #=> 2
296 # p e.next #=> 3
297 # p e.next #raises StopIteration
298 #
299 # Note that enumeration sequence by +next+ does not affect other non-external
300 # enumeration methods, unless the underlying iteration methods itself has
301 # side-effect
302 #
303 def next
304 next_values.__svalue
305 end
306
307 ##
308 # call-seq:
309 # e.next_values -> array
310 #
311 # Returns the next object as an array in the enumerator, and move the
312 # internal position forward. When the position reached at the end,
313 # StopIteration is raised.
314 #
315 # This method can be used to distinguish <code>yield</code> and <code>yield
316 # nil</code>.
317 #
318 # === Example
319 #
320 # o = Object.new
321 # def o.each
322 # yield
323 # yield 1
324 # yield 1, 2
325 # yield nil
326 # yield [1, 2]
327 # end
328 # e = o.to_enum
329 # p e.next_values
330 # p e.next_values
331 # p e.next_values
332 # p e.next_values
333 # p e.next_values
334 # e = o.to_enum
335 # p e.next
336 # p e.next
337 # p e.next
338 # p e.next
339 # p e.next
340 #
341 # ## yield args next_values next
342 # # yield [] nil
343 # # yield 1 [1] 1
344 # # yield 1, 2 [1, 2] [1, 2]
345 # # yield nil [nil] nil
346 # # yield [1, 2] [[1, 2]] [1, 2]
347 #
348 # Note that +next_values+ does not affect other non-external enumeration
349 # methods unless underlying iteration method itself has side-effect
350 #
351 def next_values
352 if @lookahead
353 vs = @lookahead
354 @lookahead = nil
355 return vs
356 end
357 raise @stop_exc if @stop_exc
358
359 curr = Fiber.current
360
361 if !@fib || !@fib.alive?
362 @dst = curr
363 @fib = Fiber.new do
364 result = each do |*args|
365 feedvalue = nil
366 Fiber.yield args
367 if @feedvalue
368 feedvalue = @feedvalue
369 @feedvalue = nil
370 end
371 feedvalue
372 end
373 @stop_exc = StopIteration.new "iteration reached an end"
374 @stop_exc.result = result
375 Fiber.yield nil
376 end
377 @lookahead = nil
378 end
379
380 vs = @fib.resume curr
381 if @stop_exc
382 @fib = nil
383 @dst = nil
384 @lookahead = nil
385 @feedvalue = nil
386 raise @stop_exc
387 end
388 vs
389 end
390
391 ##
392 # call-seq:
393 # e.peek -> object
394 #
395 # Returns the next object in the enumerator, but doesn't move the internal
396 # position forward. If the position is already at the end, StopIteration
397 # is raised.
398 #
399 # === Example
400 #
401 # a = [1,2,3]
402 # e = a.to_enum
403 # p e.next #=> 1
404 # p e.peek #=> 2
405 # p e.peek #=> 2
406 # p e.peek #=> 2
407 # p e.next #=> 2
408 # p e.next #=> 3
409 # p e.next #raises StopIteration
410 #
411 def peek
412 peek_values.__svalue
413 end
414
415 ##
416 # call-seq:
417 # e.peek_values -> array
418 #
419 # Returns the next object as an array, similar to Enumerator#next_values, but
420 # doesn't move the internal position forward. If the position is already at
421 # the end, StopIteration is raised.
422 #
423 # === Example
424 #
425 # o = Object.new
426 # def o.each
427 # yield
428 # yield 1
429 # yield 1, 2
430 # end
431 # e = o.to_enum
432 # p e.peek_values #=> []
433 # e.next
434 # p e.peek_values #=> [1]
435 # p e.peek_values #=> [1]
436 # e.next
437 # p e.peek_values #=> [1, 2]
438 # e.next
439 # p e.peek_values # raises StopIteration
440 #
441 def peek_values
442 if @lookahead.nil?
443 @lookahead = next_values
444 end
445 @lookahead.dup
446 end
447
448 ##
449 # call-seq:
450 # e.rewind -> e
451 #
452 # Rewinds the enumeration sequence to the beginning.
453 #
454 # If the enclosed object responds to a "rewind" method, it is called.
455 #
456 def rewind
457 @obj.rewind if @obj.respond_to? :rewind
458 @fib = nil
459 @dst = nil
460 @lookahead = nil
461 @feedvalue = nil
462 @stop_exc = false
463 self
464 end
465
466 ##
467 # call-seq:
468 # e.feed obj -> nil
469 #
470 # Sets the value to be returned by the next yield inside +e+.
471 #
472 # If the value is not set, the yield returns nil.
473 #
474 # This value is cleared after being yielded.
475 #
476 # # Array#map passes the array's elements to "yield" and collects the
477 # # results of "yield" as an array.
478 # # Following example shows that "next" returns the passed elements and
479 # # values passed to "feed" are collected as an array which can be
480 # # obtained by StopIteration#result.
481 # e = [1,2,3].map
482 # p e.next #=> 1
483 # e.feed "a"
484 # p e.next #=> 2
485 # e.feed "b"
486 # p e.next #=> 3
487 # e.feed "c"
488 # begin
489 # e.next
490 # rescue StopIteration
491 # p $!.result #=> ["a", "b", "c"]
492 # end
493 #
494 # o = Object.new
495 # def o.each
496 # x = yield # (2) blocks
497 # p x # (5) => "foo"
498 # x = yield # (6) blocks
499 # p x # (8) => nil
500 # x = yield # (9) blocks
501 # p x # not reached w/o another e.next
502 # end
503 #
504 # e = o.to_enum
505 # e.next # (1)
506 # e.feed "foo" # (3)
507 # e.next # (4)
508 # e.next # (7)
509 # # (10)
510 #
511 def feed(value)
512 raise TypeError, "feed value already set" if @feedvalue
513 @feedvalue = value
514 nil
515 end
516
517 # just for internal
518 class Generator
519 def initialize(&block)
520 raise TypeError, "wrong argument type #{self.class} (expected Proc)" unless block.kind_of? Proc
521
522 @proc = block
523 end
524
525 def each(*args, &block)
526 args.unshift Yielder.new(&block)
527 @proc.call(*args)
528 end
529 end
530
531 # just for internal
532 class Yielder
533 def initialize(&block)
534 raise LocalJumpError, "no block given" unless block_given?
535
536 @proc = block
537 end
538
539 def yield(*args)
540 @proc.call(*args)
541 end
542
543 def << *args
544 self.yield(*args)
545 self
546 end
547 end
548end
549
550module Kernel
551 ##
552 # call-seq:
553 # obj.to_enum(method = :each, *args) -> enum
554 # obj.enum_for(method = :each, *args) -> enum
555 # obj.to_enum(method = :each, *args) {|*args| block} -> enum
556 # obj.enum_for(method = :each, *args){|*args| block} -> enum
557 #
558 # Creates a new Enumerator which will enumerate by calling +method+ on
559 # +obj+, passing +args+ if any.
560 #
561 # If a block is given, it will be used to calculate the size of
562 # the enumerator without the need to iterate it (see Enumerator#size).
563 #
564 # === Examples
565 #
566 # str = "xyz"
567 #
568 # enum = str.enum_for(:each_byte)
569 # enum.each { |b| puts b }
570 # # => 120
571 # # => 121
572 # # => 122
573 #
574 # # protect an array from being modified by some_method
575 # a = [1, 2, 3]
576 # some_method(a.to_enum)
577 #
578 # It is typical to call to_enum when defining methods for
579 # a generic Enumerable, in case no block is passed.
580 #
581 # Here is such an example, with parameter passing and a sizing block:
582 #
583 # module Enumerable
584 # # a generic method to repeat the values of any enumerable
585 # def repeat(n)
586 # raise ArgumentError, "#{n} is negative!" if n < 0
587 # unless block_given?
588 # return to_enum(__method__, n) do # __method__ is :repeat here
589 # sz = size # Call size and multiply by n...
590 # sz * n if sz # but return nil if size itself is nil
591 # end
592 # end
593 # each do |*val|
594 # n.times { yield *val }
595 # end
596 # end
597 # end
598 #
599 # %i[hello world].repeat(2) { |w| puts w }
600 # # => Prints 'hello', 'hello', 'world', 'world'
601 # enum = (1..14).repeat(3)
602 # # => returns an Enumerator when called without a block
603 # enum.first(4) # => [1, 1, 1, 2]
604 #
605 def to_enum(meth=:each, *args)
606 Enumerator.new self, meth, *args
607 end
608 alias :enum_for :to_enum
609end
610
611module Enumerable
612 # use Enumerator to use infinite sequence
613 def zip(*arg)
614 ary = []
615 arg = arg.map{|a|a.each}
616 i = 0
617 self.each do |*val|
618 a = []
619 a.push(val.__svalue)
620 idx = 0
621 while idx < arg.size
622 begin
623 a.push(arg[idx].next)
624 rescue StopIteration
625 a.push(nil)
626 end
627 idx += 1
628 end
629 ary.push(a)
630 i += 1
631 end
632 ary
633 end
634end
Note: See TracBrowser for help on using the repository browser.