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
RevLine 
[270]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 #
[439]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
[270]119 obj = Generator.new(&block)
[439]120 elsif obj == NONE
121 raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
[270]122 end
123
124 @obj = obj
125 @meth = meth
[439]126 @args = args
[270]127 @fib = nil
128 @dst = nil
129 @lookahead = nil
130 @feedvalue = nil
131 @stop_exc = false
132 end
[439]133 attr_accessor :obj, :meth, :args
134 attr_reader :fib
[270]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 #
[439]159 def with_index(offset=0, &block)
160 return to_enum :with_index, offset unless block
161
162 if offset.nil?
163 offset = 0
[331]164 else
[439]165 offset = offset.__to_int
[331]166 end
[270]167
[331]168 n = offset - 1
169 enumerator_block_call do |*i|
[270]170 n += 1
[439]171 block.call i.__svalue, n
[270]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 #
[331]184 def each_with_index(&block)
185 with_index(0, &block)
[270]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 #
[439]216 def with_object(object, &block)
217 return to_enum(:with_object, object) unless block
[270]218
219 enumerator_block_call do |i|
[439]220 block.call [i,object]
[270]221 end
222 object
223 end
224
225 def inspect
226 if @args && @args.size > 0
227 args = @args.join(", ")
[439]228 "#<#{self.class}: #{@obj.inspect}:#{@meth}(#{args})>"
[270]229 else
[439]230 "#<#{self.class}: #{@obj.inspect}:#{@meth}>"
[270]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 #
[439]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]
[270]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
[439]283 return obj unless block
[270]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
[331]528 include Enumerable
[270]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)
[439]544 raise LocalJumpError, "no block given" unless block
[270]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
[439]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
[270]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 #
[439]626 # Here is such an example with parameter passing:
[270]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?
[439]633 # return to_enum(__method__, n) # __method__ is :repeat here
[270]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
[439]650 alias enum_for to_enum
[270]651end
652
653module Enumerable
654 # use Enumerator to use infinite sequence
[439]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
[270]677 end
[439]678 tmp.push(v)
[270]679 end
[439]680 if result.nil?
681 block.call(tmp)
682 else
683 result.push(tmp)
684 end
[270]685 end
[439]686
687 result
[270]688 end
689end
Note: See TracBrowser for help on using the repository browser.