2009-07-27 4 views
5

Czy istnieje szybki sposób zamiany płaskiej listy na listę dwóch krotek, tak że lista równa, jak [1,2,3,4,5,6], staje się [{1,2}, {3,4 }, {5,6}]?Najlepszy sposób zamiany płaskiej listy na zestaw dwóch krotek w Erlangu?

To działa, ale czuje się po prostu źle:

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). 

Odpowiedz

2

Ta wersja jest bardziej wydajny niż podejścia 'prostej' z listami konkatenacji proponowanego wcześniej:

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). 

Aby odniesienia:

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). 

wyjściowa:

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

Tak, cofanie i prefiksowanie jest zawsze wydajniejsze. Ale myślę, że podejście "proste" działa dobrze jako lekcja 1, a po niej szybko następuje odwrotność/prefiks, jak lekcja 2 ...! –

+0

Dlaczego liczyć długość listy, gdy nie robi się bardziej wrażliwe komunikaty o błędach. Twoja wstecz/2 spowoduje wyświetlenie tego samego komunikatu o błędzie, co twoja rewersja/1. To bezwartościowa praca i trochę ją spowolnić. –

+1

@Hynek: Myślę, że jest lepiej, ponieważ wcześniej zawodzi. Dodaje również jasności tego, kto przeczyta kod: – 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]). 

Jest to najlepszy i najszybszy sposób.

+0

W przypadku braku listy i bez osłony, kończy się niepowodzeniem na samym końcu przetwarzania listy, kiedy cała praca jest prawie ukończona: ** Błąd wyjątku: brak testu zgodności funkcji: tuples_from_flat_list ([5], [{3,4}, {1,2}]) – zakovyrya

+0

Tak, jest celowy. –

10

Najkrótsza i najbardziej zwięzły podejście:

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

Albo dłuższa wersja przenoszenia akumulator, ale nadal bardzo idiomatyczne 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). 

Zobacz tę sekcję w podręczniku wydajności Erlang "Myth: Tail-recursive functions are MUCH faster than recursive functions".

Jak można zauważyć, oba podejścia doprowadzą do wyjścia "badarg", gdy zostanie wywołany z listą o nierównomiernej długości. Jest to prawdopodobnie pożądane z punktu widzenia awarii.

Przeczytaj także "Myth: '++' is always bad", aby dowiedzieć się, dlaczego akumulujemy akumulator w odwrotnej kolejności, aby go odwrócić, zamiast dodawać do końca listy.

Powiązane problemy