2010-09-29 20 views
5

Ho capito lo stile di Erlang loop: coda ricorsione con funzioni che prendono tutte le "variabili che non variano":Conteggio in Erlang (? Come faccio a incrementare una variabile)

%% does something, 80 bytes at a time 
loop(Line, File) -> loop(Line, File, 0). 
loop(Line, File, Count) -> 
    do_something(Line, Count), 
    case file:read(File, 80) of 
     {ok, Line2} -> loop(Line2, File, Count + 1); 
     eof -> file:close(File); 
     {error, Reason} -> {error, Reason} 
    end. 

Ma qual è il modo migliore per incrementare un contatore in Erlang? Nella maggior parte dei linguaggi di programmazione, il modo in cui conti le cose è incrementando una variabile (ad esempio count += 1;). Le variabili di Erlang non variano, quindi dobbiamo essere creativi. Fortunatamente, abbiamo opzioni ...

Possiamo passare una variabile Contatore con le nostre funzioni e incrementarla con ogni chiamata di funzione. Possiamo usare il dizionario di processo per memorizzare un conteggio e get e put per incrementarlo. Possiamo utilizzare ETS, l'archiviazione locale dei dati per i processi. Possiamo usare un contro processo (!!!):

loop(Count) ->        
    receive         
     { incr } -> 
      loop(Count + 1);    
     { report, To } ->      
      To ! { count, Count },    
      loop(Count)       
    end.          

incr(Counter) -> 
    Counter ! { incr }. 

get_count(Counter) ->  
    Counter ! { report, self() }, 
    receive 
     { count, Count } -> Count 
    end. 

Sono sicuro che ci sono anche altri modi, a seconda dello scopo. Cosa è considerato "migliore pratica" per incrementare una variabile in Erlang?

+0

Questa domanda non è troppo significativa senza un caso d'uso. Puoi fare 'liste: foldl' per contare le cose in una lista (o' filter' + 'length'). Se contate le chiamate 'gen_server', lo fate con lo stato del vostro server facilmente. – Dustin

+0

Mi manca già PHP ... statico $ i; $ i ++; – Rolf

risposta

3

Tutto dipende da cosa si sta utilizzando il contatore. Qualsiasi cosa globale come il numero di messaggi gestiti dal sistema q dovrebbe usare ets: update_counter. Se non è globale, di solito lo includo solo nei parametri come hai mostrato.

9

Non utilizzare il dizionario di processo.

Il ciclo 'normale' che vi aspettate (vale a dire un ciclo for o un do while) di solito è implementato in una funzione ricorsiva in Erlang quindi se si sta incrementando un contatore 'normale' farlo in chiamate di funzione, come si mostra fino a.

Non utilizzare il dizionario di processo.

In caso di mancata osservazione, posso solo sottolineare che non si dovrebbe usare il dizionario di processo.

+3

Inoltre, non utilizzare il dizionario di processo. –

+2

Eppure stranamente il dizionario dei processi è utilizzato in quasi tutte le applicazioni nella distribuzione Erlang/OTP. Come "inete". O 'orber'. O 'docbuilder'. O 'ic'. O "megaco". O 'tv'. O 'cosNotification'. O "eunit'. O 'reltool'. O 'compilatore'. O "erts". Oppure 'test_server'. O "appmon". O 'ssh'. O 'debugger'. O 'kernel'. O 'gs'. O "os_mon". O 'pman'. O 'stdlib'. O 'percept'. O 'xmerl'. O 'asn1'. O "mnesia". O 'common_test'. O 'parsetools'. O "dializzatore". Oppure ... Sarebbe più facile credere nel meme "nessun dizionario di processo" se la comunità rimanesse sul messaggio. –

+3

La regola generale è che "se ti chiedi se dovresti usare il dizionario del processo, non dovresti usarlo" e "saprai quando ne avrai bisogno". Per essere onesti, mentre ci sono validi usi del dizionario di processo, molti di essi non hanno a che fare con "variabili incrementali" ma piuttosto "memorizzano i metadati del processo", per quanto ne so. –

0

Il modo standard di incrementare un contatore è come nel primo esempio. Aggiungendo una variabile alla chiamata e incrementandola. Penso che tu sia confuso dalla mancanza di loop e dalla possibilità di aggiornare i valori.

Nota che:

repeat(Times) when Times >= 0 -> repeat(0, Times). 

repeat(Times, Times) -> done; 
repeat(N, Times) -> 
    do_a_side_effect, 
    repeat(N + 1, Times). 

compila a (più o meno) la stessa cosa (in pseudo codice):

repeat(Times) -> 
    while (N < Times) { 
    do_a_side_effect 
    N++ 
    } 
    return done 

Se si vuole accumulare il risultato ci sono modi per farlo anche quello.

utilizzare il pacchetto di liste o accumulare il risultato da soli:

loop(File) -> 
    {ok, Fd} = file:open(File), 
    loop(Fd, 0, []). 

loop(Fd, Count, Acc) -> 
    case file:read(Fd, 80) of 
    {ok, Line} -> 
     Result = do_something(Line, Count), 
     loop(Fd, Count + 1, [Result | Acc]); 
    eof -> 
     file:close(File), 
     {Count, lists:reverse(Acc)}; 
    {error, Reason} -> {error, Reason} 
    end. 

o qualcosa di simile, sulla base di esempio.

Modifica: numero restituito come parte del valore restituito, poiché sembrava importante.

2

Penso che tu abbia creato un grosso problema, mentre tu puoi gestirlo molto più facilmente.
Considerate questa implementazione di ciclo for in Erlang:

for(Max, Max, F) -> [ F(Max) ]; 
for(I, Max, F) -> [ F(I) | for(I+1, Max, F) ]. 

Mentre F è la funzione che si desidera salvare di risultati per i valori I a Max.
Non è più facile da capire?

Problemi correlati