2013-05-20 7 views
8

Non capisco come il qui sotto:Qual è il punto delle fibre 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]} "} 

è molto peggio di:

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]} "} 

risposta

24

Le fibre sono un modo di sospensione e la ripresa blocchi arbitrari di codice. Un esempio come questo non è davvero un ottimo caso d'uso in quanto non offre alcun reale vantaggio rispetto al modo tradizionale di leggere le righe e elaborarle.

In questo particolare esempio, se si voleva fare meglio, devi scrivere un'interfaccia enumeratore in stile così si potrebbe scrivere:

words = WordsReader.new("testfile") 

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

dove le fibre diventano importanti è nella scrittura di codice asincrono. Ad esempio, all'interno dell'ambiente EventMachine è necessario essere in grado di emettere una chiamata asincrona, sospendere un blocco di codice e riprenderlo quando si riceve la risposta.

questo finisce per assomigliare a questo:

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 

Questa sorta di annidati, la struttura chiamata annidata e ri-nested è vagamente definito "callback inferno" come diventa molto difficile da gestire una volta che la logica diventa non- banale. Un modo per appiattire questa struttura è utilizzare fibre. Un equivalente correttamente Fiber-ized è:

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 

Le fibre possono usufruire di eccezioni, in cui il codice di callback di tipo non può. Le eccezioni, se utilizzate in modo efficace, possono semplificare in modo massivo un blocco di codice, come potete vedere qui. Invece di provare per ok? su ogni risposta, è previsto che la chiamata genererà un'eccezione di tipo NotOkay.

Le richiamate non possono generare eccezioni in modo affidabile poiché l'iniziatore della chiamata è già uscito dall'ambito quando si verifica la richiamata. Questa è una limitazione fondamentale della programmazione asincrona con i callback. Il codice basato su fibra ottica mantiene uno stack di chiamate appropriato, viene semplicemente sospeso e ripreso così com'è, quindi le eccezioni vengono trasmesse correttamente attraverso il chiamante.

Ho trovato che le fibre sono semplici da capire e molto difficili da applicare correttamente. La maggior parte delle volte non dovrai usarli direttamente, userete invece una libreria che li utilizza. Scrivere un codice "sensibile alla fibra" non è diverso dalla scrittura del codice "thread-safe". Può essere complicato avere ragione.

+4

Hai scritto tutto questo in 17 minuti? –

+3

Apparentemente così. Non sapevo di essere stato cronometrato! – tadman

+2

Wow Tadman, apprezzo molto la profondità a cui sei andato per spiegare questo a me ea tutti quelli che potrebbero incontrarlo. Lo apprezzo molto. Ciao anche da un amico canadese! :) – Senjai