2017-12-06 2 views
3

おはよう。列挙子のループマジック(ブレイクがなく、無限ループは同時に起こらない)

短い質問How do loop inside a Enumerator.new knows where to stop?

ここでさらに説明的な例です。ここでは、両方とも同じ配列を返しますコードのスニペットです:[1,2,4,8]。しかし、最初の例ではbreakにはloopという状態があり、何とか2番目の例が止まったら。

行く何
enum = Enumerator.new do |x| 
    x << "hello" 
    x << "world" 
end 

enum.take(1) 
#=> ["hello"] 
enum.take(100) 
#=> ["hello", "world"] 

break

def simple n 
    x = [] 
    a = 1 
    i = 0 
    loop do 
    x << a 
    a *= 2 
    i += 1 
    break unless i < n # in this case condition for stop used 
    end 
    x 
end 
p simple(4)== [1,2,4,8] 

例1

def enumer 
    Enumerator.new do |x| 
     a = 1 
     loop do # How do this loop know where to stop? 
     x << a 
     a *= 2 
     end 
    end 
end 
p enumer.take(4) == [1,2,4,8] 

例#2 "魔法" は

答えて

4

は、次の点を考慮していただきありがとうございますここにいる?

まあ、得られた変数xは、Enumerator::Yielderのインスタンスです。変数に<<またはyieldを呼び出すたびに、結果の最終配列に値が追加されます。

enum.take(n)は、「この列挙体の値をnまで収集しようとしています」と言っています。

だから、戻ってあなたの元の例で見て、私たちは持っていた:

loop do 
    x << a 
    a *= 2 
end 

4アイテムを収集している場合は、Enumerator::Yielderはすぐに返すことを知っているだろう可算でtake(4)と呼ばれているので。

...一方、たとえば次のコマンドを実行しようとすると、 enumer.to_aその後、ループになります。早期に終了する条件が指定されていないため、永遠に続きます。

私が見つけたことから、これがどのように機能するかについてのルビドキュメントは少し疎です。しかし、this helpful description of the behaviour in the source code

/* 
* call-seq: 
* Enumerator.new(size = nil) { |yielder| ... } 
* Enumerator.new(obj, method = :each, *args) 
* 
* Creates a new Enumerator object, which can be used as an 
* Enumerable. 
* 
* In the first form, iteration is defined by the given block, in 
* which a "yielder" object, given as block parameter, can be used to 
* yield a value by calling the +yield+ method (aliased as +<<+): 
* 
* fib = Enumerator.new do |y| 
*  a = b = 1 
*  loop do 
*  y << a 
*  a, b = b, a + b 
*  end 
* end 
* 
* p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] 
* 
* The optional parameter can be used to specify how to calculate the size 
* in a lazy fashion (see Enumerator#size). It can either be a value or 
* a callable object. 
* 
* In the second, deprecated, form, a generated Enumerator iterates over the 
* given object using the given method with the given arguments passed. 
* 
* Use of this form is discouraged. Use Kernel#enum_for or Kernel#to_enum 
* instead. 
* 
* e = Enumerator.new(ObjectSpace, :each_object) 
*  #-> ObjectSpace.enum_for(:each_object) 
* 
* e.select { |obj| obj.is_a?(Class) } #=> array of all classes 
* 
*/ 
+0

ありがとう、それは超有益であり、ありがとうございます。 –

関連する問題