2009-07-27 3 views
5

Existe-t-il un moyen rapide de convertir une liste à plat en une liste de deux-tuples telle qu'une liste plate comme [1,2,3,4,5,6] devient [{1,2}, {3,4 }, {5,6}]?Meilleur moyen de convertir une liste à un ensemble de deux-tuples dans Erlang?

Cela fonctionne, mais il se sent tout simplement faux:

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

Répondre

2

Cette version est plus efficace que l'approche 'droit' avec concaténation des listes proposées précédemment:

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

Pour référence:

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

sortie:

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

Oui, l'inversion et le préfixage sont toujours plus efficaces. Mais je pense que l'approche "straight" fonctionne bien comme la leçon 1, suivie rapidement par le reverse/prefix comme leçon 2 ...! –

+0

Pourquoi vous comptez la longueur de la liste lorsque vous ne faites pas d'erreur plus sensible msg. Votre reverse/2 déclenchera le même message d'erreur que votre reverse/1. C'est un travail inutile et le ralentir un peu. –

+1

@Hynek: Je pense que c'est mieux parce que ça échoue plus tôt. En outre, il ajoute de la clarté pour celui qui va lire le code – 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]). 

Il est le meilleur et moyen le plus rapide.

+0

En cas de liste pas égale et sans garde, il échoue à la toute fin du traitement de la liste, quand tout le travail est presque terminé: ** erreur d'exception: pas de test de correspondance de clause de fonction: tuples_from_flat_list ([5], [{3,4}, {1,2}]) – zakovyrya

+0

Oui, c'est intentionnel. –

10

L'approche la plus courte et la plus succincte:

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

Ou la version plus longue portant un accumulateur, mais encore très idiomatiques 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). 

Voir cette section dans le guide d'efficacité Erlang "Myth: Tail-recursive functions are MUCH faster than recursive functions".

Comme vous le remarquerez, les deux approches conduiront à une sortie 'badarg' lorsqu'elles seront appelées avec une liste de longueur inégale. C'est probablement souhaitable d'une perspective fail-fast.

Également lire "Myth: '++' is always bad" pour voir pourquoi nous construisons l'accumulateur à l'envers seulement pour l'inverser une fois terminé, au lieu d'ajouter à la fin de la liste.

Questions connexes