source: EcnlProtoTool/trunk/mruby-1.3.0/mrbgems/mruby-enumerator/mrblib/enumerator.rb@ 331

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

prototoolに関連するプロジェクトをnewlibからmuslを使うよう変更・更新
ntshellをnewlibの下位の実装から、muslのsyscallの実装に変更・更新
以下のOSSをアップデート
・mruby-1.3.0
・musl-1.1.18
・onigmo-6.1.3
・tcc-0.9.27
以下のOSSを追加
・openssl-1.1.0e
・curl-7.57.0
・zlib-1.2.11
以下のmrbgemsを追加
・iij/mruby-digest
・iij/mruby-env
・iij/mruby-errno
・iij/mruby-iijson
・iij/mruby-ipaddr
・iij/mruby-mock
・iij/mruby-require
・iij/mruby-tls-openssl

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