Свести список вложенных списков в Erlang - PullRequest
6 голосов
/ 19 февраля 2012

Я работаю над упражнениями в Программирование Эрланга .

Вопрос

Напишите функцию, которая при наличии списка вложенных списков будет возвращать плоский список.

Пример: flatten([[1,[2,[3],[]]], [[[4]]], [5,6]]) ⇒ [1,2,3,4,5,6].

Подсказка: используйте concatenate для решения flatten.

А вот и моя concatenate функция

%% concatenate([[1,2,3], [], [4, five]]) ⇒ [1,2,3,4,five].
concatenate([X|Xs]) -> concat(X, Xs, []).
concat([X|Xs], T, L) -> concat(Xs, T, [X|L]);
concat([], [X|Xs], L) -> concat(X, Xs, L);
concat([], [], L) -> reverse(L).

Я действительно хочу знать элегантный способ реализации flatten. Я потратил часы на решение этого упражнения.

ОБНОВЛЕНИЕ : Я забыл самую важную предпосылку. Возможно ли решить эту проблему с помощью рекурсии и сопоставления с шаблоном ?

Ответы [ 5 ]

15 голосов
/ 19 февраля 2012

Я бы попробовал вот так

flatten(X) -> lists:reverse(flatten(X,[])).

flatten([],Acc) -> Acc;
flatten([H|T],Acc) when is_list(H) -> flatten(T, flatten(H,Acc));
flatten([H|T],Acc) -> flatten(T,[H|Acc]).

тестирование

my:flatten([[1,[2,[3],[]]], [[[4]]], [5,6]]).
[1,2,3,4,5,6]

UPD: или так, без защитников и реверса, только рекурсивные вызовы и сопоставление с образцом.

flatten(X)               -> flatten(X,[]).

flatten([],Acc)          -> Acc;
flatten([[]|T],Acc)      -> flatten(T, Acc);
flatten([[_|_]=H|T],Acc) -> flatten(T, flatten(H,Acc));
flatten([H|T],Acc)       -> flatten(T,Acc++[H]) .
6 голосов
/ 19 февраля 2012

Несколько разных решений, становящихся умнее и умнее:

%% Lift nested lists to the front of the list.
flatten1([[H|T1]|T2]) -> flatten1([H,T1|T2]);
flatten1([[]|T]) -> flatten1(T);
flatten1([E|T]) -> [E|flatten1(T)];
flatten1([]) -> [].

или

%% Keep a list of things todo and put tails onto it.
flatten2(L) -> flatten2(L, []).

flatten2([H|T], Todo) ->
    flatten2(H, [T|Todo]);
flatten2([], [H|Todo]) -> flatten2(H, Todo);
flatten2([], []) -> [];
flatten2(E, Todo) -> [E|flatten2(Todo, [])].

или

%% Work from the back and keep a tail of things done.
flatten3(L) -> flatten3(L, []).

flatten3([H|T], Tail) ->
    flatten3(H, flatten3(T, Tail));
flatten3([], Tail) -> Tail;
flatten3(E, Tail) -> [E|Tail].

Это все только с сопоставлением с образцом и рекурсией, но их можно улучшить с помощью некоторых тестов типа защиты. Использование ++ неэффективно, так как каждый раз копирует список. Модуль lists использует последнюю версию с проверкой типа защиты вместо последнего предложения.

2 голосов
/ 23 ноября 2012

concatenate / 1, как определено в книге, работает как функция выравнивания, которая выравнивает только один уровень. ([[1],[2]] становится [1,2], [[[1]],[[2]]] становится [[1],[2]] и т. Д.) Стратегия, предложенная в подсказке, состоит в том, чтобы полностью сгладить не путем определения новой логики в flatten / 1, а с использованием concatenate / 1 в рекурсивном flatten / 1 вызовов.

concatenate(Ls) ->
    reverse(concatenate(Ls, [])).

concatenate([], Acc) ->
    Acc;
concatenate([[]|Rest], Acc) ->
    concatenate(Rest, Acc);
concatenate([[H|T]|Rest], Acc) ->
    concatenate([T|Rest], [H|Acc]);
concatenate([H|T], Acc) ->
    concatenate(T, [H|Acc]).

flatten(L) ->
    flatten(L, []).

flatten([], Acc) ->
    Acc;
flatten(L, Acc) ->
    Concatted = concatenate(L),
    [Non_lists|Remainder] = find_sublist(Concatted),
    flatten(Remainder, concatenate([Acc, Non_lists])).

find_sublist(L) ->
    find_sublist(L, []).

find_sublist([], Acc) ->
    reverse(Acc);
find_sublist(L = [[_|_]|_], Acc) ->
    [reverse(Acc)|L];
find_sublist([H|T], Acc) ->
    find_sublist(T, [H|Acc]).

tests() ->
    [1,2,3,4,4,5,6,7,8] = flatten([[1,[2,[3],[]]], [[[4,[4]]]], [[5],6], [[[]]], [], [[]], [[[7, 8]]]]),
    [1,2] = flatten([[1,2]]),
    [1,2,3] = flatten([[1],[2],[3]]),
    [1,2,3,4,5,6] = flatten([[1,[2,[3],[]]], [[[4]]], [5,6]]),
    tests_successful.
2 голосов
/ 20 февраля 2012

Довольно лаконичная и простая версия:

append([H | T], L) -> [H | append(T, L)];
append([], L) -> L.

flatten([[_|_]=H|T]) -> append(flatten(H), flatten(T));
flatten([[]|T]) -> flatten(T);
flatten([H|T]) -> [H|flatten(T)];
flatten([]) -> [].
0 голосов
/ 19 февраля 2012

Ключ вопроса - «разделяй и властвуй».

Еще одна дополнительная функция "lists: reverse" и оператор "++" используются для экономии времени программирования.

</p>

my_flat([],Result)->
    lists:reverse(Result);
my_flat([H|T],Result) when is_atom(H) ->
    case T of
    []-> 
        my_flat([],[H|Result]);
    _Else ->
        my_flat(T,[H|Result])
    end;
my_flat([H|T],Result) when is_number(H)->
    case T of
    []->
        my_flat([],[H|Result]);
    _Else ->
        my_flat(T,[H|Result])
    end;
my_flat([H|T],Result) ->
my_flat(H,Result)++my_flat(T,[]).

для вашего теста: test: my_flat ([[1, [2, [3], []]], [[[4]]], [5,6]], []).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...