2012-06-15 16 views
5

Ruby'nin belgelerine göre, Enumerator nesnesi, to_enum veya enum_for yöntemlerine hiçbir hedef yöntem sağlanmamışsa each yöntemini (numaralandırmak için) kullanır. Şimdi,Ruby Enumerator nesnesi, dahili bir yineleyici üzerinden dışarıdan nasıl yinelenir?

o = Object.new 
def o.each 
    yield 1 
    yield 2 
    yield 3 
end 
e = o.to_enum 

loop do 
    puts e.next 
end 

Listeleyicisi nesne gibi each yöntem bakmak çağrıları nasıl, next çağrıldığında cevap each yöntemini kullanır göz önüne alındığında, her zaman Örnek olarak, aşağıdaki maymun yama ve numaralandırıcıyı atalım next denir? Enumeartor sınıfı, o.each'un tüm içeriğini önceden yükler ve numaralandırma için yerel bir kopyasını oluşturur mu? Ya da enumeartor'da next çağrılıncaya kadar her bir verim tablosundaki işlemleri kilitleyen bir tür Ruby sihiri var mı?

Dahili bir kopya yapılırsa, bu bir kopya mı? Harici numaralandırma için kullanılabilecek G/Ç nesneleri nedir?

Ruby 1.9.2 kullanıyorum.

+2

, sen 'satır içi kod biçimlendirme yapmak metnin etrafında komutu ters tırnak (\') kullanın:) ' –

+0

muchas gracias! Bir dahaki sefere bunu akılda tutacak. –

cevap

8

Tam olarak sihir değil, ama yine de güzel. Bir tür kopyanın bir kopyasını yapmak yerine, Fiber, hedef numaralandırılabilir nesneye ilk olarak each'u yürütmek için kullanılır. each'un bir sonraki nesnesini aldıktan sonra, Fiber bu nesneyi verir ve bu nedenle, denetimi ilk olarak Fiber'un yeniden başlatıldığı yere geri döndürür.

Bu yaklaşım güzel çünkü sayısız nesnenin örneğin bir kopyasını #to_a numaralı telefondan almayı hayal edebileceği gibi bir kopya veya başka bir "yedek" biçimi gerektirmiyor. Elyaflı işbirlikli çizelgeleme, bağlamları bir bakıma saklamak zorunda kalmadan ihtiyaç duyulduğunda tam olarak değiştirmeyi sağlar.

Bu, Enumerator için C code içinde olur. kabaca aynı davranışlar sergilemiştir Saf Yakut sürümü bu gibi görünebilir: Bilesin ki

class MyEnumerator 
    def initialize(enumerable) 
    @fiber = Fiber.new do 
     enumerable.each { |item| Fiber.yield item } 
    end 
    end 

    def next 
    @fiber.resume || raise(StopIteration.new("iteration reached an end")) 
    end 
end 

class MyEnumerable 
    def each 
    yield 1 
    yield 2 
    yield 3 
    end 
end 

e = MyEnumerator.new(MyEnumerable.new) 
puts e.next # => 1 
puts e.next # => 2 
puts e.next # => 3 
puts e.next # => StopIteration is raised 
+0

Güzel! Fiberleri daha ayrıntılı olarak okuyacağım, ancak kontrol ediciyi arayan kişiye döndürmek için dil tarafından oluşturulan bu yeşil konular mı? Başka bir deyişle, kontrol nasıl geri döndü? –

+0

@SalmanParacha [Wikipedia] (http://en.wikipedia.org/wiki/Fiber_ (computer_science)), farkı açıklayabildiğimden daha iyi bir iş çıkarır. Eğer gory detaylarını istiyorsanız, uygulama [cont.c] 'de (https://github.com/ruby/ruby/blob/trunk/cont.c). – emboss

+1

@SalmanParacha: Lifler, Ruby'nin koroutinler için adıdır. Bir altprogram bir alt yordamın genelleştirilmesidir: bir altprogram * her zaman * baştan başlamaya başlar ve * her zaman * arayan kişiye geri döner. Bir coroutine, en son durduğu noktadan geçer ve sadece geldiği yerden değil, herhangi bir başka koroutine “dönüş” (veya daha kesin olarak kontrol aktarımı) yapabilir. –