Prolog flatten/2 实现

问题描述

这是我对 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).