A solution that uses an "open list" to append the elements encountered while walking the list-of-lists, which is essentially a tree, in prefix fashion.
The indicated examples indicate that non-list elements at depth 0 shall be discarded, and the other elements sorted by depth. However, no precise spec is given.
Indeed, one would expect the result of flattening
[[[[a]],b,[c]],d,e,[f]]
to be
[b,f,c,a]
via the "sorted-by-depth" pair list of Depth-Value pairs:
[3-a,1-b,2-c,1-f]
But the question poster requests this result instead:
[f, a, b, c]
I don't really know whether this is an error or not.
:- debug(flatwalker).
flatwalker(ListIn,ListOut) :-
Tip=Fin, % 2 unbound variables
% % designating the same memory
% % cell. Hold the Tip, grow at Fin.
flatwalker_2(0,ListIn,Fin,TerFin), % Elements are appended at Fin.
% % The final Fin is found in TerFin
% % on success.
TerFin=[], % Unify TerFin with [], closing
% % the list at Tip.
keysort(Tip,Sorted), % Sort the closed list at Tip
% % by pair key, i.e. by depth.
% % keysort/2 is stable and keeps
% % duplicates.
debug(flatwalker,"Got : ~q",[Tip]),
maplist([_-V,V]>>true,Sorted,ListOut). % Remove depth values.
% ---
% flatwalker_2(+Depth,+TreeIn,+Fin,+TerFin)
% Depth: Input integer, indicates current tree depth.
% TreeIn: The list to flatten at this depth (it's a node of the tree,
% which may or may not contain subtrees, i.e. lists)
% Fin: Always an unbound variable denoting the end of an open list to
% which we will append.
% ("points to an empty memory cell at the fin of the open list")
% Works as an accumulator as a new Fin, advanced by 1 cell at each
% append operation is handed to the next flatwalker_2/4
% activation.
% TerFin: When flatwalker_2/ is done, the final Fin is unified with
% TerFin so that it can be passed to flatwalker/2.
% ---
% We make the guards explicit and cut heavily.
% Optimizing the guards (if so desired) is left as an exercise.
flatwalker_2(_,[],Fin,Fin) :- !. % Done as TreeIn is empty.
% Unify Fin with TerFin.
flatwalker_2(0,[X|Xs],Fin,TerFin) :- % Case of X is nonlist at depth 0:
% % discard!
\+is_list(X),!,
flatwalker_2(0,Xs,Fin,TerFin). % Continue with the rest of the
% list at this depth.
flatwalker_2(D,[X|Xs],Fin,TerFin) :- % Case of X is nonlist at
% % depth > 0: keep!
D>0,\+is_list(X),!,
Fin=[D-X|Fin2], % Grow the result list at its
% % Fin by D-X.
flatwalker_2(D,Xs,Fin2,TerFin). % Continue with the rest of the
% list at this depth.
flatwalker_2(D,[X|Xs],Fin,TerFin) :- % Case of X is a list at any
% % depth.
is_list(X),!,
DD is D+1,
flatwalker_2(DD,X,Fin,Fin2), % Collect one level down
flatwalker_2(D,Xs,Fin2,TerFin). % On return, continue with the
% rest of the list at this depth.
Some plunit
tests:
:- begin_tests(flatwalker).
test("empty",true(Out == [])) :-
flatwalker([],Out).
test("simple",true(Out == [])) :-
flatwalker([1,2,3],Out).
test("with empties",true(Out == [])) :-
flatwalker([[],1,[],2,[],3,[]],Out).
test("test 1",true(Out == [a, b, c, d, e])) :-
flatwalker([1,2,[a,b,c],3,4,[d,e]],Out).
test("test 2",true(Out == [a, b])) :-
flatwalker([1,[],2,3,4,[a,b]],Out).
test("test 3",true(Out == [f, a])) :-
flatwalker([[[[a]]],b,c,d,e,[f]],Out).
test("test 4",true(Out == [f, a, b, c])) :-
flatwalker([[[[a]],b,[c]],d,e,[f]],Out).
:- end_tests(flatwalker).
And so:
?- run_tests.
% PL-Unit: flatwalker
% Got : []
.
% Got : []
.
% Got : []
.
% Got : [1-a,1-b,1-c,1-d,1-e]
.
% Got : [1-a,1-b]
.
% Got : [3-a,1-f]
.
% Got : [3-a,1-b,2-c,1-f]
ERROR: flatwalker.pl:66:
test test 4: wrong answer (compared using ==)
ERROR: Expected: [f,a,b,c]
ERROR: Got: [b,f,c,a]
done
% 1 test failed
% 6 tests passed
false.