私はプロローグと数日間しか働いていません。私はいくつかのことを理解していますが、これは本当に私を混乱させます。
リストを受け取り、それをフラット化する関数を作成するとします。
?- 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!
なぜそれは一方では機能するが、他方では機能しないのですか?とてもシンプルなものが足りないような気がします。
あなたが与えた_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]
_または空のリスト_[]
_に再帰的に削減し、破棄します。次に、それらを蓄積し、それらをすべて1つのリストに追加して出力します。
ほとんどのProlog実装では、空のリスト_[]
_はatom andリストなので、atom([])
への呼び出しはとis_list([])
はどちらもtrueと評価されます。これは、文字アトムとは対照的に、空のリストを破棄するのに役立ちません。
開始へのポインタと「終了ホール⁄フリーポインタ」(つまり、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
を使う必要がなくなります。この手法は「差分リスト」と呼ばれますが、代わりに「オープンエンドリスト」と考える方がはるかに簡単です。
update:これは 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 のコメントにより、両方のバージョンが簡略化されました!)
プロローグのリスト表記は、非常に単純なプロローグ用語の上に構文糖を付けたものです。プロローグリストは次のように示されます。
空のリストはatom _[]
_で表されます。どうして?それは空のリストの数学的表記のように見えるからです。空のリストを示すためにnil
のようなatomを使用することもできましたが、そうしませんでした。
空でないリストは、_.\2
_という用語で表されます。最初の(左端の)引数はリストのheadで、2番目は(右端の)引数は、リストのtailです。これは、再帰的に、それ自体がリストです。
いくつかの例:
空のリスト:_[]
_はatomとして表されます。
_[]
_
1つの要素のリスト_[a]
_は、
_.(a,[])
_
2つの要素のリスト_[a,b]
_は、
_.(a,.(b,[]))
_
3つの要素のリスト、_[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( [_|_] ) .
_
完全性のためのアキュムレータベースのバージョンは次のとおりです。
% 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.
_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)
describeは同じソリューションを持っていますが、ソリューションの順序が異なります。検討してください:
_?- 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
_
他の述語がなく、末尾再帰のみ。
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([], []).
findall
を使用した解決策が見つからなかったため、追加します。 (リストがグラウンドの場合は機能します)
まず、リストをテストする方法を定義します。
list(X) :- var(X), !, fail.
list([]).
list([_|_]).
member
の- 推移的閉包 をmember*
と呼びます。
'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].