2013-05-20 3 views
8

Ich verstehe nicht, wie die folgenden:Was ist der Sinn von Fasern in Ruby?

counts = Hash.new(0) 

File.foreach("testfile") do |line| 
    line.scan(/\w+/) do |word| 
    word = word.downcase 
    counts[word] += 1 
    end 
end 

counts.keys.sort.each {|k| print "#{k}:#{counts[k]} "} 

so viel schlechter ist als:

words = Fiber.new do 
    File.foreach("testfile") do |line| 
    line.scan(/\w+/) do |word| 
     Fiber.yield word.downcase 
    end 
    end 
end 

counts = Hash.new(0) 

while word = words.resume 
    counts[word] += 1 
end 

counts.keys.sort.each {|k| print "#{k}:#{counts[k]} "} 

Antwort

24

Fasern sind eine Möglichkeit zur Aussetzung und willkürlichen Code-Blöcke wieder aufzunehmen. Ein solches Beispiel ist nicht wirklich ein großartiger Anwendungsfall, da es keinen wirklichen Vorteil gegenüber der traditionellen Art des Lesens und Verarbeitens von Zeilen bietet.

In diesem speziellen Beispiel, wenn Sie es besser machen wollen, dann würden Sie eine Enumerator-Stil-Schnittstelle schreiben, so dass Sie schreiben können:

words = WordsReader.new("testfile") 

words.each do |word| 
    # ... 
end 

Wo Faser wichtig worden asynchronen Code ist in schriftlicher Form. Zum Beispiel müssen Sie innerhalb der EventMachine-Umgebung in der Lage sein, einen asynchronen Anruf auszuführen, einen Codeblock auszusetzen und ihn fortzusetzen, wenn Sie die Antwort erhalten.

Dieser endet wie folgt aussehen:

async_call(argument1, argument2) do |response_1| 
    if (response_1.ok?) 
    async_call(argument3, argument4) do |response_2| 
     if (response_2.ok?) 
     async_call(argument5, argument6) do |response_3| 
      if (response_3.ok?) 
      do_something(response_1, response_2, response_3) 
      else 
      panic_and_fail! 
      end 
     end 
     else 
     panic_and_fail! 
     end 
    end 
    else 
    panic_and_fail! 
    end 
end 

Diese Art von verschachtelten, verschachtelten und Wieder verschachtelten Aufrufstruktur lose als „Callback-Hölle“, da es sehr schwierig wird zu verwalten, sobald Ihre Logik nicht wird trivial. Eine Möglichkeit, diese Struktur abzuflachen, besteht darin, Fasern zu verwenden. Ein richtig Fiber-ized entspricht:

begin 
    response_1 = fiber_call(argument1, argument2) 
    response_2 = fiber_call(argument3, argument4) 
    response_3 = fiber_call(argument5, argument6) 

    do_something(response_1, response_2, response_3) 

rescue NotOkay 
    panic_and_fail! 
end 

Fibers Vorteil von Ausnahmen nehmen, wo Rückruf-Typ-Code nicht. Ausnahmen können, wenn sie effektiv verwendet werden, einen Codeblock massiv vereinfachen, wie Sie hier sehen können. Anstatt für ok? für jede Antwort zu testen, wird erwartet, dass der Aufruf stattdessen eine Ausnahme vom Typ auslöst.

Callbacks können keine Ausnahmen zuverlässig auslösen, da der Initiator des Aufrufs beim Rückruf bereits nicht mehr in den Geltungsbereich fällt. Dies ist eine grundlegende Einschränkung der asynchronen Programmierung mit Rückrufen. Fiber-gesteuerter Code behält einen richtigen Call-Stack bei, er wird lediglich angehalten und unverändert fortgesetzt, so dass Exceptions ordnungsgemäß durch den Aufrufer kaskadieren.

Ich habe Fibres gefunden, um sowohl einfach zu verstehen als auch sehr schwierig zu sein, richtig anzuwenden. In den meisten Fällen müssen Sie sie nicht direkt verwenden. Sie verwenden stattdessen eine Bibliothek, die sie verwendet. Das Schreiben von "Fiber-aware" Code ist dem Schreiben von "Thread-safe" Code nicht unähnlich. Es kann schwierig sein, richtig zu handeln.

+4

Sie haben das alles in 17 Minuten geschrieben? –

+3

Anscheinend so. Wusste nicht, dass ich Zeit hatte! – tadman

+2

Wow Tadman, ich schätze wirklich die Tiefe, in die du gegangen bist, um mir das und jeden, der darauf stoßen könnte, zu erklären. Ich schätze es sehr. Hallo von einem Landsmann auch! :) – Senjai