2009-07-27 17 views
5

¿Existe una forma rápida de convertir una lista plana en una lista de dos tuplas de modo que una lista plana como [1,2,3,4,5,6] se convierta en [{1,2}, {3,4 }, {5,6}]?¿La mejor manera de convertir una lista plana en un conjunto de dos tuplas en Erlang?

Esto funciona, pero se siente mal simplemente:

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

Respuesta

2

Esta versión es más eficiente que el enfoque de 'recto' con listas de concatenación propuesto anteriormente:

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

Para referencia:

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

de salida:

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

Sí, la inversión y el prefijo siempre son más eficaces. ¡Pero creo que el enfoque "directo" funciona bien como la Lección 1, seguida rápidamente por el reverso/prefijo como la Lección 2 ...! –

+0

¿Por qué cuenta la longitud de la lista cuando no hace un mensaje de error más sensible? Su reverse/2 generará el mismo mensaje de error que su reverse/1. Es un trabajo sin valor y ralentizarlo un poco. –

+1

@Hynek: Creo que es mejor porque falla antes. También agrega claridad para alguien que va a leer el código – 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]). 

Es mejor y más rápida es.

+0

En caso de que ni siquiera aparezcan en la lista y sin protección, falla al final del procesamiento de la lista, cuando todo el trabajo está casi terminado: ** error de excepción: ninguna prueba de concordancia de cláusula de función: tuples_from_flat_list ([5], [{3,4}, {1,2}]) – zakovyrya

+0

Sí, es intencional. –

10

El enfoque más corta y más sucinta:

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

O la versión más larga que lleva un acumulador, pero sigue siendo muy idiomática 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). 

Consulte esta sección en la guía de la eficiencia Erlang "Myth: Tail-recursive functions are MUCH faster than recursive functions".

Como notará, ambos enfoques darán lugar a una salida 'badarg' cuando se llame con una lista de longitud desigual. Esto es probablemente deseable desde una perspectiva de falla.

Lea también "Myth: '++' is always bad" para ver por qué acumulamos el acumulador en reversa solo para invertirlo cuando termina, en lugar de agregarlo al final de la lista.

Cuestiones relacionadas