问题描述
这是我对 flatten/2
的实现:
flt([],[]).
flt([H|L],[H|X]):-
not(is_list(H)),flt(L,X).
flt([H|L],X):-
append(R,F,X),flt(H,R),F).
给出了预期的结果:
?- flt([1,[2,3,[4,5],6],7],X).
X = [1,2,4,5,6,7]
但是,当我点击 ;
时超出了堆栈限制。为什么会发生这种情况?无限递归在哪里?
谢谢。
解决方法
问题是您调用 append(R,F,X)
时仅使用 未实例化 变量。
flt(+List,-Flatlist)
只有一种解决方案。然而,在回溯时,append(R,X)
被一次又一次地尝试,却没有找到其他解决方案......
你可以通过询问来测试这个
?- append(R,X).
R = [],F = X ;
R = [_948],X = [_948|F] ;
R = [_948,_960],X = [_948,_960|F] ;
R = [_948,_960,_972],_972|F] ;
R = [_948,_972,_984],_984|F] ;
R = [_948,_984,_996],_996|F]
...
解决方案是重新排列第三个子句中的目标:
flt([H|L],X):-
flt(H,R),flt(L,F),append(R,X).
这是一个很好的例子,说明 Prolog 不是纯粹的声明性,因为它通过简单的时间顺序回溯来实现解析会强制语言的过程特征。
为了说明问题,请查看此跟踪(为了强调循环问题,我跳过了很多):
[trace] ?- flt([[a]],X).
Call: (8) flt([[a]],_680) ? creep
^ Call: (9) not(is_list([a])) ? creep
^ Fail: (9) not(user:is_list([a])) ? creep
Redo: (8) flt([[a]],_680) ? creep
Call: (9) lists:append(_904,_906,_680) ? creep
Exit: (9) lists:append([],_680,_680) ? creep
Call: (9) flt([a],[]) ? skip
Fail: (9) flt([a],[]) ? creep
Redo: (9) lists:append(_904,_680) ? creep
Exit: (9) lists:append([_890],_898,[_890|_898]) ? creep
Call: (9) flt([a],[_890]) ? skip
Exit: (9) flt([a],[a]) ? creep
Call: (9) flt([],_898) ? skip
Exit: (9) flt([],[]) ? creep
Exit: (8) flt([[a]],[a]) ? creep
X = [a] ;
Redo: (9) flt([a],[_890]) ? skip
Fail: (9) flt([a],[_890]) ? creep
Redo: (9) lists:append([_890|_892],_918,[_890|_898]) ? creep
Exit: (9) lists:append([_890,_902],_910,[_890,_902|_910]) ? creep
Call: (9) flt([a],_902]) ? skip
Fail: (9) flt([a],_902]) ? creep
Redo: (9) lists:append([_890,_902|_904],_930,_902|_910]) ? creep
Exit: (9) lists:append([_890,_902,_914],_922,_914|_922]) ? creep
Call: (9) flt([a],_914]) ? skip
Fail: (9) flt([a],_914]) ? creep
Redo: (9) lists:append([_890,_914|_916],_942,_914|_922]) ? creep
Exit: (9) lists:append([_890,_914,_926],_934,_926|_934]) ? creep
Call: (9) flt([a],_926]) ? skip
Fail: (9) flt([a],_926]) ? creep
Redo: (9) lists:append([_890,_926|_928],_954,_926|_934]) ? creep
Exit: (9) lists:append([_890,_926,_938],_946,_938|_946]) ? creep
Call: (9) flt([a],_938]) ? skip
Fail: (9) flt([a],_938]) ? creep
Redo: (9) lists:append([_890,_938|_940],_966,_938|_946]) ? creep
Exit: (9) lists:append([_890,_938,_950],_958,_950|_958]) ? creep
Call: (9) flt([a],_950]) ? skip
Fail: (9) flt([a],_950]) ? creep
Redo: (9) lists:append([_890,_950|_952],_978,_950|_958]) ?
此图以图形方式显示。要求其他答案后会发生什么情况会显示为红色。
另一种可能的解决方案是添加剪辑:
flt([],[]).
flt([H|L],[H|X]):-
not(is_list(H)),X),!.
flt([H|L],X):-
append(R,flt(H,!,F).