让我们假设我们正在读取标准输入并构建已读取的所有行的列表。最后,我们需要显示那些以逗号分隔的行。
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 ).
此代码的问题是append
中的processInput/3
调用会花费我们O(n)。我们怎样才能避免这种成本和仍然让我们的谓词是尾递归的(因为我们可能从标准输入中读取了很多行)?
我想到我们可以用以下内容替换append
。
LinesSoFar1 = [ Line | LinesSoFar ],
我们可以在显示之前反转列表。但这看起来很糟糕。还有更好的方法吗?
答案 0 :(得分:7)
我不考虑你提出的解决方案(在列表元素之前,然后在最后反转列表)“hacky”。具有显式差异列表的gusbro解决方案也是可以的。我认为最优雅的方法是使用DCG表示法(差异列表的隐式接口),即使用描述行列表的DCG:
read_lines -->
{ read_line_to_codes(current_input, Codes) },
( { Codes == end_of_file } -> []
; { atom_codes(Line, Codes) },
[Line],
read_lines
).
用法:phrase(read_lines, Lines)
。
答案 1 :(得分:2)
您可以使用半实例化结构来完成此操作。 检查此代码:
append_init(Marker-Marker).
append_end(L-[I|NMarker], I, L-NMarker).
append_finish(L-[], L).
首先通过调用 append_init(L)来“初始化”半实例化结构。 然后,您可以通过调用 append_end(L,Item,NewList)在列表末尾添加元素。 完成添加元素后,可以调用 append_finish(L,List)来获取最终的,完全实例化的列表。
示例:
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].