Home > Software design >  Get elements of arbitrary nested lists
Get elements of arbitrary nested lists

Time:10-03

I am looking for some predicate in SWI-Prolog to get the elements of some arbitrary nested list. Means, if I e.g. have the list:

L = [[a,b], c, [d, [e, f]]]

I get as result:

R = [a,b,c,d,e,f]

CodePudding user response:

As @brebs mentioned in his comment, Use predefined predicate flatten/2

% ?- flatten([[a,b], c, [d, [e, f]]], R).
%     R = [a, b, c, d, e, f]

This user-defined implementation is similar to the predefined one [1]

my_flatten([],[]).
my_flatten([H|T], [H|Res]) :- \  is_list(H), my_flatten(T, Res), !.
my_flatten([H|T], Res) :- my_flatten(H, Res). % H is list.

[1] except for cases of non-termination like my_flatten(X,non_list). and like my_flatten([X],[1,2,3,4]). thanks to @false comment

CodePudding user response:

The SWI built-in predicate flatten/2 depends on the very instantiations of the first argument. It thus leads to quite non-relational behavior:

?-         flatten(X,[]).
   false.
?- X = [], flatten(X,[]).
   X = [].
?- X = [[],[]], flatten(X,[]).
   X = [[], []].
?- X = [[]|[]], flatten(X,[]).
   X = [[]].

Note that there are infinitely many X to make flatten(X,[]) succeed. If you want this to be a relation, there are two choices either enumerate all such solutions, or produce an instantiation error, or just do not terminate (better than an incorrect answer), or delay goals appropriately, or produce some constraints, or produce a resource error. Oh, these have been now 6 choices...

In most of such situations, the easiest way to go is to produce instantiation errors like so:

flattened(T) -->
   {functor(T,_,_)},  % ensures instantiation
   (  {T = [E|Es]} -> flattened(E), flattened(Es)
   ;  {T = []} -> []
   ;  [T]
   ).
?- phrase(flattened([[]|[]]),Xs).
   Xs = [].
?- phrase(flattened([[]|_]),Xs).
   error(instantiation_error,functor/3).
  • Related