2009-07-27 14 views
5

C'è un modo veloce per convertire una lista semplice in una lista di due tuple in modo tale che una lista semplice come [1,2,3,4,5,6] diventa [{1,2}, {3,4 }, {5,6}]?Il modo migliore per convertire una lista piatta in un set di due tuple in Erlang?

Questo funziona, ma ci si sente SBAGLIATO semplicemente:

tuples_from_flat_list(Target) -> 
    TargetWithIndex = lists:zip(lists:seq(1, length(Target)), Target), 
    {K, V} = lists:partition(fun({I, _}) -> I rem 2 == 1 end, TargetWithIndex), 
    lists:zipwith(fun({_, X}, {_, Y}) -> {X, Y} end, K, V). 

risposta

2

Questa versione è più efficiente di approccio 'dritti' con le liste concatenazione proposto in precedenza:

combine(L) when length(L) rem 2 == 0 -> combine([], L). 
combine(Acc, []) -> lists:reverse(Acc); 
combine(Acc, [H1,H2|T]) -> combine([{H1, H2}|Acc], T). 

benchmark:

combine.erl

-module(combine). 
-export([reverse/1, straight/1, test/2]). 

test(F, L) -> {Micros, _} = timer:tc(?MODULE, F, [L]), Micros. 

reverse(L) when length(L) rem 2 == 0 -> reverse([], L).         
straight(L) when length(L) rem 2 == 0 -> straight([], L). 

reverse(Acc, []) -> lists:reverse(Acc); 
reverse(Acc, [H1, H2 | T]) -> reverse([{H1, H2} | Acc], T). 

straight(Acc, []) -> Acc; 
straight(Acc, [H1, H2 | T]) -> straight(AcC++ [{H1, H2}], T). 

uscita:

130> combine:test(reverse, lists:seq(1,1000)). 
34 
131> combine:test(straight, lists:seq(1,1000)). 
1772 
+0

Sì, l'inversione e il prefisso sono sempre più efficienti. Ma penso che l'approccio "dritto" funzioni bene come lezione 1, seguito rapidamente dal prefisso/inverso come lezione 2 ...! –

+0

Perché si conta la lunghezza dell'elenco quando non si esegue un messaggio di errore più sensibile. Il tuo inverso/2 genererà lo stesso messaggio di errore del tuo contrario/1. È un lavoro inutile e rallenta un po '. –

+1

@Hynek: Penso che sia meglio perché fallisce prima. Inoltre aggiunge chiarezza a chi leggerà il codice – zakovyrya

3
tuples_from_flat_list(List) -> tuples_from_flat_list(List, []). 

tuples_from_flat_list([], Result) -> lists:reverse(Result). 
tuples_from_flat_list([X, Y|T], Acc) -> tuples_from_flat_list(T, [{X, Y}|Acc]). 

E 'meglio e più veloce.

+0

In caso di mancata lista e senza guardia, fallisce alla fine dell'elaborazione della lista, quando tutto il lavoro è quasi finito: ** errore di eccezione: nessuna funzione di test clausola di allineamento: tuples_from_flat_list ([5], [{3,4}, {1,2}]) – zakovyrya

+0

Sì, è intenzionale. –

10

L'approccio più breve e più concisa:

pair_up([A, B | Tail]) -> 
    [{A,B} | pair_up(Tail)]; 
pair_up([]) -> 
    []. 

O la versione più lunga che trasporta un accumulatore, ma ancora molto idiomatica Erlang:

pair_up(List) -> 
    pair_up(List, []). 

pair_up([A, B | Tail], Acc) -> 
    pair_up(Tail, [{A,B} | Acc]); 
pair_up([], Acc) -> 
    lists:reverse(Acc). 

Vedere questa sezione nella guida efficienza erlang "Myth: Tail-recursive functions are MUCH faster than recursive functions".

Come si noterà, entrambi gli approcci determinano un'uscita "badarg" quando vengono chiamati con un elenco di lunghezze irregolari. Questo è probabilmente auspicabile da una prospettiva a prova di errore.

Leggere anche "Myth: '++' is always bad" per capire perché accumuliamo l'accumulatore al contrario solo per invertire quando viene eseguito, anziché accodare alla fine dell'elenco.

Problemi correlati