How does an Enumerator stop in the middle of a method?

(Example from an episode of Ruby Tapas. 59)

@names = %w[Ylva Brighid Shifra Yesamin]

def names
  yield @names.shift
  yield @names.shift
  yield @names.shift
  yield @names.shift
end

enum = to_enum(:names)
enum.next # => Ylva
@names    # => ["Brighid", "Shifra", "Yesamin"]

namesThe execution of the method stops after the first line. If namesfully executed, @namesshould be empty. How can this magic happen (= method call partially)?

+4
source share
2 answers

It works as expected. In an enum.next call, it calls the first line in the name method and then returns to the caller, i.e. Stops the execution flow of the name method at this point. The next time enum.next is called, the thread of execution is taken from the point where it stopped.

Ruby Fiber, : http://apidock.com/ruby/Fiber " " , Fiber.yield resume, .

, :

@names = %w[Ylva Brighid Shifra Yesamin]
fiber = Fiber.new do
  Fiber.yield @names.shift # yields control to the caller
  Fiber.yield @names.shift
  Fiber.yield @names.shift
  Fiber.yield @names.shift
end

# the resume calls give control to the fiber at the point we left off
puts fiber.resume #=> Ylva
puts fiber.resume #=> Brighid
puts fiber.resume #=> Shifra
puts fiber.resume #=> Yesamin
+1

Object # to_enum ( , Object#enum_for) , :

obj.to_enum(method = :each, *args)

" Enumerator, , method obj, args, . Object, , , each :

enum = 1.to_enum
enum.each { |i| puts "i" }
  #NoMethodError: undefined method `each' for 1:Fixnum

to_enum, :each, :

obj.to_enum

, obj . :each, , self, main.

enum, each , enum ( ) .

, :

a = [1,2,3]
enum = a.to_enum
  #=> #<Enumerator: [1, 2, 3]:each> 
enum.to_a
  #=> [1, 2, 3] 
enum.each { |e| puts e }
  #-> 1
  #   2
  #   3
  #=> [1, 2, 3] 
a[0] = 'cat'
enum.to_a
  #=> ["cat", 2, 3] 
a.object_id
  #=> 70235487149000
a = []
a.object_id
  #=> 70235487117180
enum.to_a
  #=> ["cat", 2, 3] !!
a = [1,2,3]
enum = a.to_enum
a.replace([])
enum.to_a
  #=> [] 

, #=>, , , #->, , .

loop do

, :

enum = [1,2,3].to_enum
  #=> #<Enumerator: [1, 2, 3]:each>

enum, Enumerator # next :

enum.next #=> 1 
enum.next #=> 2 
enum.next #=> 3 
enum.next #=> StopIteration: iteration reached an end
enum.rewind 
enum.next #=> 1

, StopIteration , .

a > , loop StopIteration, . :

enum = [1,2,3].to_enum
loop do
  puts enum.next
end
  #-> 1
  #   2
  #   3
  #=> nil 

names ,

, , , @names ( "" ). :

def names
  yield "Lucy"
  s = "Billy-Bob"
  yield s
end

, :

def names
  yield "Lucy"
  s = "Billy-Bob"
  yield s
end

names { |s| puts "My name is #{s}" }
  #-> My name is Lucy
  #   My name is Billy-Bob

:

enum = to_enum(:names)
  #=> #<Enumerator: main:names> 

, Enumerator # next:

enum.next #=> "Lucy" 
enum.next #=> "Billy-Bob" 
enum.next #=> StopIteration: iteration reached an end (exception)

, ? Ruby names , yield.

each enum , :

enum.each { |s| puts "My name is #{s}" }
  #-> My name is Lucy
  #   My name is Billy-Bob

each enum .

names,

, .

@names = %w[Ylva Brighid Shifra Yesamin]

def names
  yield @names.shift
  yield @names.shift
  yield @names.shift
  yield @names.shift
end

, names :

names { |s| puts "My name is #{s}" }
  #-> My name is Ylva
  #   My name is Brighid
  #   My name is Shifra
  #   My name is Yesamin

:

@names #=> []

@names:

@names = %w[Ylva Brighid Shifra Yesamin]

names:

enum = to_enum(:names)
  #=> #<Enumerator: main:names>

next @names:

enum.next # => @names.shift => "Ylva"
  # => "Ylva"

next Ruby yield names , . : `

@names    #=> ["Brighid", "Shifra", "Yesamin"]

:

enum.next #=> "Brighid" 
@names    #=> ["Shifra", "Yesamin"] 

enum.next #=> "Shifra" 
@names    #=> ["Yesamin"] 

enum.next #=> "Yesamin" 
@names    #=> [] 

:

enum.next #StopIteration: iteration reached an end

, , -, :

enum.to_a
  #=> [nil, nil, nil, nil]

, :

[][0]   #=> nil
[][1]   #=> nil
[][999] #=> nil

each enum , :

@names = %w[Ylva Brighid Shifra Yesamin]
enum.each { |s| puts "My name is #{s}" }
  #-> My name is Ylva
  #   My name is Brighid
  #   My name is Shifra
  #   My name is Yesamin

, , @names, , ?

+1

All Articles