Avoid linear cost of append/3 in Prolog
Let's assume that we're reading from standard input and building a list of all the开发者_StackOverflow中文版 lines that have been read. In the end, we need to display those lines, separated by commas.
go:-
prompt(_, ''),
processInput([ ], Lines),
findall(_, (member(L, Lines), write(L), write(',')), _),
nl.
processInput(LinesSoFar, Lines):-
read_line_to_codes(current_input, Codes),
processInput(Codes, LinesSoFar, Lines).
processInput(Codes, LinesSoFar, Lines):-
( Codes \= end_of_file
->
atom_codes(Line, Codes),
append(LinesSoFar, [ Line ], LinesSoFar1), % <---- append/3 - O(n)
processInput(LinesSoFar1, Lines)
;
Lines = LinesSoFar ).
The issue w/ this code is that the append
call in processInput/3
costs us O(n). How can we avoid this cost & still let our predicate be tail-recursive (because we may be reading a lot of lines from standard input)?
It occurred to me that we could replace the append
with the following.
LinesSoFar1 = [ Line | LinesSoFar ],
And we could reverse the list before displaying it. But that seems hacky. Is there a better way?
I do not consider the solution you propose (prepending list elements, then reversing the list at the end) "hacky". gusbro's solution with explicit difference lists is OK as well. I think the most elegant way is to use DCG notation (an implicit interface to difference lists), i.e., use a DCG that describes the list of lines:
read_lines -->
{ read_line_to_codes(current_input, Codes) },
( { Codes == end_of_file } -> []
; { atom_codes(Line, Codes) },
[Line],
read_lines
).
Usage: phrase(read_lines, Lines)
.
You can do it using a semi instantiated structure. Check this code:
append_init(Marker-Marker).
append_end(L-[I|NMarker], I, L-NMarker).
append_finish(L-[], L).
You start by 'initializing' the semi-instantiated structure by calling append_init(L). Then you can add elements at the end of the list by calling append_end(L, Item, NewList). When you have finished adding elements you call append_finish(L, List) to get the final, fully-instantiated list.
Example:
example(NL):-
append_init(L),
append_end(L, a, L1),
append_end(L1, b, L2),
append_end(L2, c, L3),
append_finish(L3, NL).
?- example(L).
L = [a, b, c].
精彩评论