我只与Prolog合作了几天。我理解了一些事情,但这让我很困惑。
我想要编写一个带有列表并展平它的函数。
?- flatten([a,[b,c],[[d],[],[e]]],Xs).
Xs = [a,b,c,d,e]. % expected result
该函数取出列表的内部结构。
这是我到目前为止所做的:
flatten2([],[]).
flatten2([Atom|ListTail],[Atom|RetList]) :-
atom(Atom), flatten2(ListTail,RetList).
flatten2([List|ListTail],RetList) :-
flatten2(List,RetList).
现在,这在我打电话时起作用了:
?- flatten2([a,[b,c],[[d],[],[e]]], R).
R = [a,b,c,d,e]. % works as expected!
但是当我打电话来查看我输入的列表是否已经展平时,会返回false
而不是true
:
?- flatten2([a,[b,c],[[d],[],[e]]], [a,b,c,d,e]).
false. % BAD result!
为什么一方面工作,另一方面又不工作?我觉得我错过了很简单的事情。
答案 0 :(得分:19)
你给出的flatten2/2
的定义被破坏了;它实际上表现得像这样:
?- flatten2([a, [b,c], [[d],[],[e]]], R).
R = [a, b, c] ;
false.
因此,考虑到您已将R
绑定到[a,b,c,d,e]
的情况,失败并不奇怪。
您的定义是丢弃第3个子句中的列表尾部(ListTail
) - 这需要整理并连接回列表以通过RetList
返回。这是一个建议:
flatten2([], []) :- !.
flatten2([L|Ls], FlatL) :-
!,
flatten2(L, NewL),
flatten2(Ls, NewLs),
append(NewL, NewLs, FlatL).
flatten2(L, [L]).
这一个递归地将所有列表列表减少到单个项目列表[x]
或它丢弃的空列表[]
。然后,它累积并将它们全部附加到输出中的所有列表中。
请注意,在大多数Prolog实现中,空列表[]
是一个原子和一个列表,因此对atom([])
和is_list([])
的调用都会评估为真;这不会帮助你丢弃空名单而不是字符原子。
答案 1 :(得分: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 --->
然后将其称为
flatten2( A, B):- flatten2( A, B, []).
这样就无需在任何地方使用reverse
。这种技术被称为“差异列表”,但更容易将其视为“开放式列表”。
更新:使用dcg语法编码更容易。由于它是单向的(第一个参数必须完全接地),为什么不使用切割:
flattn([]) --> [], !.
flattn([A|T]) --> {\+is_list(A)}, [A], !, flattn(T).
flattn([A|T]) --> flattn(A), flattn(T).
测试:
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.
如果定义是完全声明的,那么最后一个也应该成功地使用X=[c] ; X=[[],c] ; ... ; X=[[c]] ; ...
;唉,事实并非如此。
( edit2 :简化了两个版本,感谢@ mat的评论!)
答案 2 :(得分:1)
Prolog的列表符号是非常简单的prolog术语之上的语法糖。 Prolog列表如此表示:
空列表由原子[]
表示。为什么?因为这看起来像空列表的数学符号。他们可以使用像nil
这样的原子来表示空列表,但他们没有。
非空列表由术语.\2
表示,其中第一个(最左边)参数是列表的 head ,第二个(最右边)参数是列表的 tail ,递归地,本身就是一个列表。
一些例子:
空列表:[]
表示为原子:
[]
一个元素的列表[a]
在内部存储为
.(a,[])
两个元素[a,b]
的列表在内部存储为
.(a,.(b,[]))
三个元素的列表[a,b,c]
在内部存储为
.(a,.(b,.(c,[])))
对列表头部的检查同样是用相同符号表示的语法糖:
[X|Xs]
与.(X,Xs)
[A,B|Xs]
与.(A,.(B,Xs))
[A,B]
(见上文)与.(A,.(B,[]))
相同
我自己,我会这样写flatten/2
:
%------------------------
% public : flatten a list
%------------------------
flatten( X , R ) :-
flatten( X , [] , T ) ,
reverse( T , R )
.
%--------------------------------------------
% private : flatten a list into reverse order
%--------------------------------------------
flatten( [] , R , R ) . % the empty list signals the end of recursion
flatten( [X|Xs] , T , R ) :- % anything else is flattened by
flatten_head( X , T , T1 ) , % - flattening the head, and
flatten( Xs , T1 , R ) % - flattening the tail
. %
%-------------------------------------
% private : flatten the head of a list
%-------------------------------------
flatten_head( X , T , [X|T] ) :- % if the head is a not a list
\+ list(X) , % - simply prepend it to the accumulator.
! . %
flatten_head( X , T , R ) :- % if the head is a list
flatten( X , T , R ) % - recurse down and flatten it.
.
%-----------------------
% what's a list, anyway?
%-----------------------
list( X ) :- var(X) , ! , fail .
list( [] ) .
list( [_|_] ) .
答案 3 :(得分:1)
以下是基于累加器的完整性版本:
% flatten/2
flatten(List, Result) :- flatten(List, [], Result).
% auxiliary predicate flatten/3
flatten([], Result, Result).
flatten([Head| Tail], Part, Result) :-
is_list(Head),
!,
flatten(Head, HR),
append(Part, HR, PR),
flatten(Tail, PR, Result).
flatten([Head| Tail], Part, Result) :-
append(Part, [Head], PR),
flatten(Tail, PR, Result).
flatten(X, Part, Result) :-
fail.
答案 4 :(得分:1)
在if_//3
和list_truth/2
的基础上,我们可以按如下方式实施myflatten/2
:
myflatten(Xs,Ys) :-
phrase(myflatten_aux(Xs),Ys).
myflatten_aux([]) --> [].
myflatten_aux([T|Ts]) -->
if_(neither_nil_nor_cons_t(T), [T], myflatten_aux(T)),
myflatten_aux(Ts).
:- use_module(library(dialect/sicstus/block)).
:- block neither_nil_nor_cons(-).
neither_nil_nor_cons(X) :-
\+nil_or_cons(X).
nil_or_cons([]).
nil_or_cons([_|_]).
neither_nil_nor_cons_t(X,Truth) :-
( nonvar(X)
-> ( neither_nil_nor_cons(X) -> Truth = true
; Truth = false
)
; nonvar(Truth)
-> ( Truth == true -> neither_nil_nor_cons(X)
; Truth == false, nil_or_cons(X)
)
; Truth = true, neither_nil_nor_cons(X)
; Truth = false, nil_or_cons(X)
).
示例查询(取自其他答案,以及对答案的评论):
?- myflatten([[4],[[5,6],[7,[8],[9,[10,11]]]]], Xs).
Xs = [4, 5, 6, 7, 8, 9, 10, 11].
?- myflatten([1,[8,3],[3,[5,6],2],8], Xs).
Xs = [1, 8, 3, 3, 5, 6, 2, 8].
?- myflatten([a,[b,c],[],[[[d]]]], Xs).
Xs = [a, b, c, d].
?- myflatten([a,[b,c],[[d],[],[e]]], Xs).
Xs = [a, b, c, d, e].
neither_nil_nor_cons_t
和not(nil_or_cons_t)
描述了相同的解决方案,但解决方案顺序不同。考虑:
?- myflatten([A,B,C],Xs), A=a,B=b,C=c.
A = a,
B = b,
C = c,
Xs = [a, b, c] ; % does not terminate universally
答案 5 :(得分:0)
我没有找到使用list(X) :- var(X), !, fail.
list([]).
list([_|_]).
的解决方案,所以我会添加它。 (如果列表是基础,它将起作用)
首先,我们定义如何测试列表:
member
以及member*
的{{3}},我们称之为'member*'(X, Y) :- member(X, Y).
'member*'(X, Y) :- member(Z, Y), 'member*'(X, Z).
:
member*
展平列表是flatten(X, Y) :- findall(Z, ('member*'(Z, X), \+ list(Z)), Y).
的所有解决方案,不是列表:
?- flatten([[4],[[5,6],[7,[8],[9,[10,11]]]]],Y).
Y = [4, 5, 6, 7, 8, 9, 10, 11].
示例:
p.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
答案 6 :(得分:0)
没有任何其他谓词,只有尾递归。
flatten([[X|S]|T], F) :- flatten([X|[S|T]], F).
flatten([[]|S], F) :- flatten(S, F).
flatten([X|S], [X|T]) :- \+(X = []), \+(X = [_|_]), flatten(S, T).
flatten([], []).