source: EcnlProtoTool/trunk/mruby-2.1.1/mrbgems/mruby-enumerator/mrblib/enumerator.rb@ 439

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

mrubyを2.1.1に更新

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-ruby;charset=UTF-8
File size: 16.9 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(obj, method = :each, *args)
93 #
94 # Creates a new Enumerator object, which can be used as an
95 # Enumerable.
96 #
97 # In the first form, iteration is defined by the given block, in
98 # which a "yielder" object, given as block parameter, can be used to
99 # yield a value by calling the +yield+ method (aliased as +<<+):
100 #
101 # fib = Enumerator.new do |y|
102 # a = b = 1
103 # loop do
104 # y << a
105 # a, b = b, a + b
106 # end
107 # end
108 #
109 # p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
110 #
111 # In the second, deprecated, form, a generated Enumerator iterates over the
112 # given object using the given method with the given arguments passed. This
113 # form is left only for internal use.
114 #
115 # Use of this form is discouraged. Use Kernel#enum_for or Kernel#to_enum
116 # instead.
117 def initialize(obj=NONE, meth=:each, *args, &block)
118 if block
119 obj = Generator.new(&block)
120 elsif obj == NONE
121 raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
122 end
123
124 @obj = obj
125 @meth = meth
126 @args = args
127 @fib = nil
128 @dst = nil
129 @lookahead = nil
130 @feedvalue = nil
131 @stop_exc = false
132 end
133 attr_accessor :obj, :meth, :args
134 attr_reader :fib
135
136 def initialize_copy(obj)
137 raise TypeError, "can't copy type #{obj.class}" unless obj.kind_of? Enumerator
138 raise TypeError, "can't copy execution context" if obj.fib
139 @obj = obj.obj
140 @meth = obj.meth
141 @args = obj.args
142 @fib = nil
143 @lookahead = nil
144 @feedvalue = nil
145 self
146 end
147
148 ##
149 # call-seq:
150 # e.with_index(offset = 0) {|(*args), idx| ... }
151 # e.with_index(offset = 0)
152 #
153 # Iterates the given block for each element with an index, which
154 # starts from +offset+. If no block is given, returns a new Enumerator
155 # that includes the index, starting from +offset+
156 #
157 # +offset+:: the starting index to use
158 #
159 def with_index(offset=0, &block)
160 return to_enum :with_index, offset unless block
161
162 if offset.nil?
163 offset = 0
164 else
165 offset = offset.__to_int
166 end
167
168 n = offset - 1
169 enumerator_block_call do |*i|
170 n += 1
171 block.call i.__svalue, n
172 end
173 end
174
175 ##
176 # call-seq:
177 # e.each_with_index {|(*args), idx| ... }
178 # e.each_with_index
179 #
180 # Same as Enumerator#with_index(0), i.e. there is no starting offset.
181 #
182 # If no block is given, a new Enumerator is returned that includes the index.
183 #
184 def each_with_index(&block)
185 with_index(0, &block)
186 end
187
188 ##
189 # call-seq:
190 # e.each_with_object(obj) {|(*args), obj| ... }
191 # e.each_with_object(obj)
192 # e.with_object(obj) {|(*args), obj| ... }
193 # e.with_object(obj)
194 #
195 # Iterates the given block for each element with an arbitrary object, +obj+,
196 # and returns +obj+
197 #
198 # If no block is given, returns a new Enumerator.
199 #
200 # @example
201 # to_three = Enumerator.new do |y|
202 # 3.times do |x|
203 # y << x
204 # end
205 # end
206 #
207 # to_three_with_string = to_three.with_object("foo")
208 # to_three_with_string.each do |x,string|
209 # puts "#{string}: #{x}"
210 # end
211 #
212 # # => foo:0
213 # # => foo:1
214 # # => foo:2
215 #
216 def with_object(object, &block)
217 return to_enum(:with_object, object) unless block
218
219 enumerator_block_call do |i|
220 block.call [i,object]
221 end
222 object
223 end
224
225 def inspect
226 if @args && @args.size > 0
227 args = @args.join(", ")
228 "#<#{self.class}: #{@obj.inspect}:#{@meth}(#{args})>"
229 else
230 "#<#{self.class}: #{@obj.inspect}:#{@meth}>"
231 end
232 end
233
234 ##
235 # call-seq:
236 # enum.each { |elm| block } -> obj
237 # enum.each -> enum
238 # enum.each(*appending_args) { |elm| block } -> obj
239 # enum.each(*appending_args) -> an_enumerator
240 #
241 # Iterates over the block according to how this Enumerator was constructed.
242 # If no block and no arguments are given, returns self.
243 #
244 # === Examples
245 #
246 # Array.new(3) #=> [nil, nil, nil]
247 # Array.new(3) { |i| i } #=> [0, 1, 2]
248 # Array.to_enum(:new, 3).to_a #=> [0, 1, 2]
249 # Array.to_enum(:new).each(3).to_a #=> [0, 1, 2]
250 #
251 # obj = Object.new
252 #
253 # def obj.each_arg(a, b=:b, *rest)
254 # yield a
255 # yield b
256 # yield rest
257 # :method_returned
258 # end
259 #
260 # enum = obj.to_enum :each_arg, :a, :x
261 #
262 # enum.each.to_a #=> [:a, :x, []]
263 # enum.each.equal?(enum) #=> true
264 # enum.each { |elm| elm } #=> :method_returned
265 #
266 # enum.each(:y, :z).to_a #=> [:a, :x, [:y, :z]]
267 # enum.each(:y, :z).equal?(enum) #=> false
268 # enum.each(:y, :z) { |elm| elm } #=> :method_returned
269 #
270 def each(*argv, &block)
271 obj = self
272 if 0 < argv.length
273 obj = self.dup
274 args = obj.args
275 if !args.empty?
276 args = args.dup
277 args.concat argv
278 else
279 args = argv.dup
280 end
281 obj.args = args
282 end
283 return obj unless block
284 enumerator_block_call(&block)
285 end
286
287 def enumerator_block_call(&block)
288 @obj.__send__ @meth, *@args, &block
289 end
290 private :enumerator_block_call
291
292 ##
293 # call-seq:
294 # e.next -> object
295 #
296 # Returns the next object in the enumerator, and move the internal position
297 # forward. When the position reached at the end, StopIteration is raised.
298 #
299 # === Example
300 #
301 # a = [1,2,3]
302 # e = a.to_enum
303 # p e.next #=> 1
304 # p e.next #=> 2
305 # p e.next #=> 3
306 # p e.next #raises StopIteration
307 #
308 # Note that enumeration sequence by +next+ does not affect other non-external
309 # enumeration methods, unless the underlying iteration methods itself has
310 # side-effect
311 #
312 def next
313 next_values.__svalue
314 end
315
316 ##
317 # call-seq:
318 # e.next_values -> array
319 #
320 # Returns the next object as an array in the enumerator, and move the
321 # internal position forward. When the position reached at the end,
322 # StopIteration is raised.
323 #
324 # This method can be used to distinguish <code>yield</code> and <code>yield
325 # nil</code>.
326 #
327 # === Example
328 #
329 # o = Object.new
330 # def o.each
331 # yield
332 # yield 1
333 # yield 1, 2
334 # yield nil
335 # yield [1, 2]
336 # end
337 # e = o.to_enum
338 # p e.next_values
339 # p e.next_values
340 # p e.next_values
341 # p e.next_values
342 # p e.next_values
343 # e = o.to_enum
344 # p e.next
345 # p e.next
346 # p e.next
347 # p e.next
348 # p e.next
349 #
350 # ## yield args next_values next
351 # # yield [] nil
352 # # yield 1 [1] 1
353 # # yield 1, 2 [1, 2] [1, 2]
354 # # yield nil [nil] nil
355 # # yield [1, 2] [[1, 2]] [1, 2]
356 #
357 # Note that +next_values+ does not affect other non-external enumeration
358 # methods unless underlying iteration method itself has side-effect
359 #
360 def next_values
361 if @lookahead
362 vs = @lookahead
363 @lookahead = nil
364 return vs
365 end
366 raise @stop_exc if @stop_exc
367
368 curr = Fiber.current
369
370 if !@fib || !@fib.alive?
371 @dst = curr
372 @fib = Fiber.new do
373 result = each do |*args|
374 feedvalue = nil
375 Fiber.yield args
376 if @feedvalue
377 feedvalue = @feedvalue
378 @feedvalue = nil
379 end
380 feedvalue
381 end
382 @stop_exc = StopIteration.new "iteration reached an end"
383 @stop_exc.result = result
384 Fiber.yield nil
385 end
386 @lookahead = nil
387 end
388
389 vs = @fib.resume curr
390 if @stop_exc
391 @fib = nil
392 @dst = nil
393 @lookahead = nil
394 @feedvalue = nil
395 raise @stop_exc
396 end
397 vs
398 end
399
400 ##
401 # call-seq:
402 # e.peek -> object
403 #
404 # Returns the next object in the enumerator, but doesn't move the internal
405 # position forward. If the position is already at the end, StopIteration
406 # is raised.
407 #
408 # === Example
409 #
410 # a = [1,2,3]
411 # e = a.to_enum
412 # p e.next #=> 1
413 # p e.peek #=> 2
414 # p e.peek #=> 2
415 # p e.peek #=> 2
416 # p e.next #=> 2
417 # p e.next #=> 3
418 # p e.next #raises StopIteration
419 #
420 def peek
421 peek_values.__svalue
422 end
423
424 ##
425 # call-seq:
426 # e.peek_values -> array
427 #
428 # Returns the next object as an array, similar to Enumerator#next_values, but
429 # doesn't move the internal position forward. If the position is already at
430 # the end, StopIteration is raised.
431 #
432 # === Example
433 #
434 # o = Object.new
435 # def o.each
436 # yield
437 # yield 1
438 # yield 1, 2
439 # end
440 # e = o.to_enum
441 # p e.peek_values #=> []
442 # e.next
443 # p e.peek_values #=> [1]
444 # p e.peek_values #=> [1]
445 # e.next
446 # p e.peek_values #=> [1, 2]
447 # e.next
448 # p e.peek_values # raises StopIteration
449 #
450 def peek_values
451 if @lookahead.nil?
452 @lookahead = next_values
453 end
454 @lookahead.dup
455 end
456
457 ##
458 # call-seq:
459 # e.rewind -> e
460 #
461 # Rewinds the enumeration sequence to the beginning.
462 #
463 # If the enclosed object responds to a "rewind" method, it is called.
464 #
465 def rewind
466 @obj.rewind if @obj.respond_to? :rewind
467 @fib = nil
468 @dst = nil
469 @lookahead = nil
470 @feedvalue = nil
471 @stop_exc = false
472 self
473 end
474
475 ##
476 # call-seq:
477 # e.feed obj -> nil
478 #
479 # Sets the value to be returned by the next yield inside +e+.
480 #
481 # If the value is not set, the yield returns nil.
482 #
483 # This value is cleared after being yielded.
484 #
485 # # Array#map passes the array's elements to "yield" and collects the
486 # # results of "yield" as an array.
487 # # Following example shows that "next" returns the passed elements and
488 # # values passed to "feed" are collected as an array which can be
489 # # obtained by StopIteration#result.
490 # e = [1,2,3].map
491 # p e.next #=> 1
492 # e.feed "a"
493 # p e.next #=> 2
494 # e.feed "b"
495 # p e.next #=> 3
496 # e.feed "c"
497 # begin
498 # e.next
499 # rescue StopIteration
500 # p $!.result #=> ["a", "b", "c"]
501 # end
502 #
503 # o = Object.new
504 # def o.each
505 # x = yield # (2) blocks
506 # p x # (5) => "foo"
507 # x = yield # (6) blocks
508 # p x # (8) => nil
509 # x = yield # (9) blocks
510 # p x # not reached w/o another e.next
511 # end
512 #
513 # e = o.to_enum
514 # e.next # (1)
515 # e.feed "foo" # (3)
516 # e.next # (4)
517 # e.next # (7)
518 # # (10)
519 #
520 def feed(value)
521 raise TypeError, "feed value already set" if @feedvalue
522 @feedvalue = value
523 nil
524 end
525
526 # just for internal
527 class Generator
528 include Enumerable
529 def initialize(&block)
530 raise TypeError, "wrong argument type #{self.class} (expected Proc)" unless block.kind_of? Proc
531
532 @proc = block
533 end
534
535 def each(*args, &block)
536 args.unshift Yielder.new(&block)
537 @proc.call(*args)
538 end
539 end
540
541 # just for internal
542 class Yielder
543 def initialize(&block)
544 raise LocalJumpError, "no block given" unless block
545
546 @proc = block
547 end
548
549 def yield(*args)
550 @proc.call(*args)
551 end
552
553 def << *args
554 self.yield(*args)
555 self
556 end
557 end
558
559 ##
560 # call-seq:
561 # Enumerator.produce(initial = nil) { |val| } -> enumerator
562 #
563 # Creates an infinite enumerator from any block, just called over and
564 # over. Result of the previous iteration is passed to the next one.
565 # If +initial+ is provided, it is passed to the first iteration, and
566 # becomes the first element of the enumerator; if it is not provided,
567 # first iteration receives +nil+, and its result becomes first
568 # element of the iterator.
569 #
570 # Raising StopIteration from the block stops an iteration.
571 #
572 # Examples of usage:
573 #
574 # Enumerator.produce(1, &:succ) # => enumerator of 1, 2, 3, 4, ....
575 #
576 # Enumerator.produce { rand(10) } # => infinite random number sequence
577 #
578 # ancestors = Enumerator.produce(node) { |prev| node = prev.parent or raise StopIteration }
579 # enclosing_section = ancestors.find { |n| n.type == :section }
580 def Enumerator.produce(init=NONE, &block)
581 raise ArgumentError, "no block given" if block.nil?
582 Enumerator.new do |y|
583 if init == NONE
584 val = nil
585 else
586 val = init
587 y.yield(val)
588 end
589 begin
590 while true
591 y.yield(val = block.call(val))
592 end
593 rescue StopIteration
594 # do nothing
595 end
596 end
597 end
598end
599
600module Kernel
601 ##
602 # call-seq:
603 # obj.to_enum(method = :each, *args) -> enum
604 # obj.enum_for(method = :each, *args) -> enum
605 #
606 # Creates a new Enumerator which will enumerate by calling +method+ on
607 # +obj+, passing +args+ if any.
608 #
609 # === Examples
610 #
611 # str = "xyz"
612 #
613 # enum = str.enum_for(:each_byte)
614 # enum.each { |b| puts b }
615 # # => 120
616 # # => 121
617 # # => 122
618 #
619 # # protect an array from being modified by some_method
620 # a = [1, 2, 3]
621 # some_method(a.to_enum)
622 #
623 # It is typical to call to_enum when defining methods for
624 # a generic Enumerable, in case no block is passed.
625 #
626 # Here is such an example with parameter passing:
627 #
628 # module Enumerable
629 # # a generic method to repeat the values of any enumerable
630 # def repeat(n)
631 # raise ArgumentError, "#{n} is negative!" if n < 0
632 # unless block_given?
633 # return to_enum(__method__, n) # __method__ is :repeat here
634 # end
635 # each do |*val|
636 # n.times { yield *val }
637 # end
638 # end
639 # end
640 #
641 # %i[hello world].repeat(2) { |w| puts w }
642 # # => Prints 'hello', 'hello', 'world', 'world'
643 # enum = (1..14).repeat(3)
644 # # => returns an Enumerator when called without a block
645 # enum.first(4) # => [1, 1, 1, 2]
646 #
647 def to_enum(meth=:each, *args)
648 Enumerator.new self, meth, *args
649 end
650 alias enum_for to_enum
651end
652
653module Enumerable
654 # use Enumerator to use infinite sequence
655 def zip(*args, &block)
656 args = args.map do |a|
657 if a.respond_to?(:each)
658 a.to_enum(:each)
659 else
660 raise TypeError, "wrong argument type #{a.class} (must respond to :each)"
661 end
662 end
663
664 result = block ? nil : []
665
666 each do |*val|
667 tmp = [val.__svalue]
668 args.each do |arg|
669 v = if arg.nil?
670 nil
671 else
672 begin
673 arg.next
674 rescue StopIteration
675 nil
676 end
677 end
678 tmp.push(v)
679 end
680 if result.nil?
681 block.call(tmp)
682 else
683 result.push(tmp)
684 end
685 end
686
687 result
688 end
689end
Note: See TracBrowser for help on using the repository browser.