2013-04-25 3 views
11

Я познакомился с Эрланом Армстронгами «Программирование Эрланг». Одно упражнение - написать повторную реализацию tuple_to_list/1 BIF. Мое решение кажется мне неэлегантным, особенно из-за вспомогательной функции, которую я использую. Есть ли еще Erlang-ish способ сделать это?Erlang: элегантный tuple_to_list/1

tup2lis({}) -> []; 
tup2lis(T) -> tup2list_help(T,1,tuple_size(T)). 

tup2list_help(T,Size,Size) -> [element(Size,T)]; 
tup2list_help(T,Pos,Size) -> [element(Pos,T)|tup2list_help(T,Pos+1,Size)]. 

Большое спасибо за ваши идеи. :)

+1

Будьте в курсе, какой из нижеприведенные реализации являются хвостовыми рекурсивными. – Tilman

+3

Нет ничего плохого в том, что у вас есть вспомогательные функции, часто они вам нужны, и они - лучший способ сделать что-то. И не беспокойтесь о хвостовой рекурсии, см. Http://www.erlang.org/doc/efficiency_guide/listHandling.html#id64720 – rvirding

ответ

17

Я думаю, что ваша функция в порядке, и больше, если ваша цель - изучить язык. Как правило, базовый случай при построении списков - это только пустой список []. Так что я бы написать

tup2list(Tuple) -> tup2list(Tuple, 1, tuple_size(Tuple)). 

tup2list(Tuple, Pos, Size) when Pos =< Size -> 
    [element(Pos,Tuple) | tup2list(Tuple, Pos+1, Size)]; 
tup2list(_Tuple,_Pos,_Size) -> []. 

вы можете написать довольно много же с пониманием список

[element(I,Tuple) || I <- lists:seq(1,tuple_size(Tuple))]. 

он будет работать, как и ожидалось, когда кортеж не имеет элементов, как списки: SEQ (1,0) дает пустой список.

+3

+1 для понимания списка. Мне нравится использование списков: seq. Чувствую для меня немного пифонов. ;) – Zakum

7

Ваш код хорош, а также идиоматический способ создания такого рода материалов. Вы также можете создать этот список назад, который в этом случае будет немного быстрее из-за хвостового вызова, но не значителен.

tup2list(T) -> tup2list(T, size(T), []). 

tup2list(T, 0, Acc) -> Acc; 
tup2list(T, N, Acc) -> tup2list(T, N-1, [element(N,T)|Acc]). 
+1

Лучше использовать 'tuple_size/1' вместо' size/1'. См. Здесь: http://erlang.org/doc/efficiency_guide/commoncaveats.html – coffeMug

2

В Erlang R16B вы также можете использовать erlang:delete_element/2 функцию:

tuple2list({}) -> []; 
tuple2list(T) when is_tuple(T) -> 
    [element(1, T) | tuple2list(erlang:delete_element(1, T))]. 
+0

+1 для избавления от вспомогательной функции и необходимости использования счетчика и аккумулятора. Я читал, что ускорение хвостовой рекурсии против head-recursion в текущих версиях больше зависит от архитектуры процессора *, поэтому я ухожу, как это решение! * http: //www.erlang.org/doc/efficiency_guide/myths.html – Zakum

+4

-1 для решения, которое создает новый кортеж, когда элемент добавляется в список. Помните, что в erlang нет измененных данных, а 'erlang: delete_element/2' создает новый кортеж! – rvirding

+0

@ Zakum: Вы должны прочитать эту мифическую демистификацию более тщательно. _body-recursive list function и tail-recursive function, которая вызывает 'list: reverse/1' в конце, будет использовать точно такой же объем памяти. Обратите внимание на« lists: reverse/1 », которые моя версия не делает. Речная версия хвоста хвоста определенно будет быстрее, чем рекурсивная репликация тела [ppolv] (http://stackoverflow.com/users/171116/ppolv) [решение] (http://stackoverflow.com/a/16222818/49197), без сомнений. Но когда большинство кортежей малы, не имеет значения. Но знай разницу. –

0

Erlang 17.0, вы должны построить список в естественном порядке, решения выше неверно с точки эффективности. Всегда добавлять элементы в голову существующего списка:

%% ==================================================================== 
%% API functions 
%% ==================================================================== 
my_tuple_to_list({}) -> 
    []; 
my_tuple_to_list(Tuple) -> 
    tuple_to_list_iter(1, size(Tuple), Tuple, []) 
. 
%% ==================================================================== 
%% Internal functions 
%% ==================================================================== 
tuple_to_list_iter(N, N, Tuple, List) -> 
    lists:reverse([element(N, Tuple)|List]); 

tuple_to_list_iter(N, Tuplesize, Tuple, List) -> 
    L = [element(N, Tuple)|List], 
    tuple_to_list_iter(N + 1, Tuplesize, Tuple, L)  
. 
0

mytuple_to_list(T) when tuple_size(T) =:= 0 -> []; mytuple_to_list(T) -> [element(1, T)|mytuple_to_list(erlang:delete_element(1, T))].

3

Я пытаю упражнения с Джо Армстронг книги, вот что я придумал

my_tuple_to_list(Tuple) -> [element(T, Tuple) || T <- lists:seq(1, tuple_size(Tuple))]. 
Смежные вопросы