[英]Flatten a list of nested lists in Erlang
我正在研究Erlang編程中的練習。
問題是
編寫一個函數,給定一個嵌套列表列表,將返回一個平面列表。
示例:
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
的優雅方式。 我花了好幾個小時來解決這個問題。
更新 :我忘記了最重要的先決條件。 只有遞歸和模式匹配才能解決這個問題嗎?
我會這樣試試
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]) .
一些不同的解決方案,變得更聰明,更聰明:
%% 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
模塊使用最后一個版本的版本,帶有保護類型測試而不是最后一個子句。
本書中定義的concatenate / 1作為flatten函數,僅在一個級別上展平。 ( [[1],[2]]
變為[1,2]
, [[[1]],[[2]]]
變為[[1],[2]]
等。)提示中建議的策略是完全扁平化不是通過在flatten / 1中定義新邏輯而是在flatten / 1的遞歸調用中使用concatenate / 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.
非常簡潔明了的版本:
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([]) -> [].
問題的關鍵是“分而治之”。
另一個額外的功能“列表:反向”和操作符“++”用於節省編程時間。
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,[]).
為你的測試:測試:my_flat([[1,[2,[3],[]]],[[[4]]],[5,6]],[])。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.