在Prolog中展平列表

Toa*_*ows 24 list prolog flatten dcg difference-lists

我只与Prolog合作了几天.我理解一些事情,但这真让我感到困惑.

我想要编写一个带有列表并将其展平的函数.

?- flatten([a,[b,c],[[d],[],[e]]],Xs).  
Xs = [a,b,c,d,e].                           % expected result
Run Code Online (Sandbox Code Playgroud)

该函数取出列表的内部结构.

这是我到目前为止:

flatten2([],[]).
flatten2([Atom|ListTail],[Atom|RetList]) :-
      atom(Atom), flatten2(ListTail,RetList).
flatten2([List|ListTail],RetList) :-
      flatten2(List,RetList).
Run Code Online (Sandbox Code Playgroud)

现在,这在我打电话时起作用:

?- flatten2([a,[b,c],[[d],[],[e]]], R).
R = [a,b,c,d,e].                         % works as expected!
Run Code Online (Sandbox Code Playgroud)

但是当我打电话来查看我输入的列表是否已经展平时,将返回false而不是true:

?- flatten2([a,[b,c],[[d],[],[e]]], [a,b,c,d,e]).
false.                                   % BAD result!
Run Code Online (Sandbox Code Playgroud)

为什么一方面工作,另一方面又不工作?我觉得我错过了很简单的事情.

sha*_*rky 22

flatten2/2你给出的定义是破坏的; 它实际上表现得像这样:

?- flatten2([a, [b,c], [[d],[],[e]]], R).
R = [a, b, c] ;
false. 
Run Code Online (Sandbox Code Playgroud)

因此,鉴于在那里你已经绑定的情况下R,以[a,b,c,d,e],失败也就不足为奇了.

你的定义是扔掉ListTail第三个子句中的lists()的尾部- 这需要整理并连接回列表以返回通过RetList.这是一个建议:

flatten2([], []) :- !.
flatten2([L|Ls], FlatL) :-
    !,
    flatten2(L, NewL),
    flatten2(Ls, NewLs),
    append(NewL, NewLs, FlatL).
flatten2(L, [L]).
Run Code Online (Sandbox Code Playgroud)

这一个递归地将所有列表列表减少到单个项目列表[x][]它丢弃的空列表.然后,它累积并将它们全部附加到输出中的一个列表中.

请注意,在大多数Prolog实现中,空列表[]是一个原子一个列表,因此调用to atom([])并且is_list([])都评估为true; 这不会帮助你抛弃空列表而不是字符原子.

  • `!`是Prolog中名为_cut_的特殊字符.它告诉解释器削减(忽略)其他选择以防止回溯.有关更多信息,[现在学习Prolog!](http://www.learnprolognow.org/lpnpage.php?pagetype=html&pageid=lpn-htmlse43)有一个很好的教程. (2认同)

Wil*_*ess 8

您可以使用指向其开头的指针和结尾处"结束孔/自由指针"(即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 --->
Run Code Online (Sandbox Code Playgroud)

然后你把它称为

flatten2( A, B):- flatten2( A, B, []).
Run Code Online (Sandbox Code Playgroud)

这样就没有必要在reverse任何地方使用.这种技术被称为"差异列表",但将其视为"开放式列表"则更容易.


更新:使用语法编写更容易.由于它是单向的(第一个参数必须完全接地),为什么不使用切割:

flattn([]) --> [], !.
flattn([A|T]) --> {\+is_list(A)}, [A], !, flattn(T).
flattn([A|T]) --> flattn(A), flattn(T).
Run Code Online (Sandbox Code Playgroud)

测试:

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.
Run Code Online (Sandbox Code Playgroud)

如果定义完全是声明性的,那么最后一个也应该成功X=[c] ; X=[[],c] ; ... ; X=[[c]] ; ...; 唉,事实并非如此.

(EDIT2:简体两种版本,感谢@ 的评论!)