Вы можете поддерживать свои списки открытым, как с указателем на его начало, так и с «конечным отверстием и свободным указателем» (т. Е. Logvar) на его конце, который затем можно создать, когда конец достигнут:
flatten2( [], Z, Z):- !. % ---> X
flatten2( [Atom|ListTail], [Atom|X], Z) :- % .
\+is_list(Atom), !, % .
flatten2( ListTail, X, Z). % Y
flatten2( [List|ListTail], X, Z) :- % .
flatten2( List, X, Y), % from X to Y, and then % .
flatten2( ListTail, Y, Z). % from Y to Z % Z --->
Затем вы называете это
flatten2( A, B):- flatten2( A, B, []).
Таким образом, нет необходимости использовать reverse
в любом месте. Этот метод известен как «списки различий», но гораздо проще думать о нем как о «открытых списках».
обновление: Это гораздо проще кодировать с использованием синтаксиса dcg . Поскольку он однонаправленный (первый аргумент должен быть полностью заземлен), почему бы не использовать сокращения в конце концов:
flattn([]) --> [], !.
flattn([A|T]) --> {\+is_list(A)}, [A], !, flattn(T).
flattn([A|T]) --> flattn(A), flattn(T).
Тестирование:
16 ?- phrase(flattn([a,[b,c],[[d],[],[e]]]), [a, b, c, d, e]).
true.
17 ?- phrase(flattn([a,[b,c],[[d],[],[e]]]), R).
R = [a, b, c, d, e].
18 ?- phrase(flattn([a,[b,X],[[d],[],[e]]]), [a, b, c, d, e]).
X = c.
Если определение было полностью декларативным, последнее также должно было бы быть успешным с X=[c] ; X=[[],c] ; ... ; X=[[c]] ; ...
; увы это не так.
( edit2 : упрощены обе версии благодаря комментариям @ mat !)