この質問はかなり古いですが、とにかく私の解決策を投稿します。私は余暇にプロローグを学んでいますが、これは非常に難しい問題であることがわかりました。
DCG と差分リストについて多くのことを学びました。残念ながら、リストを使用しない解決策は思いつきませんでした。マットが示唆したように、括弧に対処するために用語をフラットリストに変換しpermutation/2
、リストを一致させるために使用して、++演算子の可換性を説明します...
これが私が思いついたものです:
:- op(900, yfx, equ).
:- op(800, xfy, ++).
check(A equ B) :- A equ B.
equ(A,B) :- sum_pp(A,AL,Len), sum_pp(B,BL,Len), !, permutation(AL, BL).
sum_pp(Term, List, Len) :- sum_pp_(Term, List,[], 0,Len).
sum_pp_(A, [A|X],X, N0,N) :- nonvar(A), A\=(_++_), N is N0+1.
sum_pp_(A, [A|X],X, N0,N) :- var(A), N is N0+1.
sum_pp_(A1++A2, L1,L3, N0,N) :-
sum_pp_(A1, L1,L2, N0,N1), (nonvar(N), N1>=N -> !,fail; true),
sum_pp_(A2, L2,L3, N1,N).
述語は、sum_pp/3
項を取り、それを被加数のフラットなリストに変換し、長さを返す (またはチェックする) 一方で、括弧の影響を受けない主力です。これは DCG ルール (差分リストを使用) に非常に似ていますが、長さを使用して、無限再帰につながる左再帰を破るのに役立つため、通常の述語として記述されています (それを打ち負かすのにかなりの時間がかかりました)。 .
グランド タームをチェックできます。
?- sum_pp(((a++b)++x++y)++c++d, L, N).
L = [a,b,x,y,c,d],
N = 6 ;
false.
また、ソリューションも生成します。
?- sum_pp((b++X++y)++c, L, 5).
X = (_G908++_G909),
L = [b,_G908,_G909,y,c] ;
false.
?- sum_pp((a++X++b)++Y, L, 5).
Y = (_G935++_G936),
L = [a,X,b,_G935,_G936] ;
X = (_G920++_G921),
L = [a,_G920,_G921,b,Y] ;
false.
?- sum_pp(Y, L, N).
L = [Y],
N = 1 ;
Y = (_G827++_G828),
L = [_G827,_G828],
N = 2 ;
Y = (_G827++_G836++_G837),
L = [_G827,_G836,_G837],
N = 3 .
演算子はequ/2
2 つの項を「統合」し、変数がある場合は解を提供することもできます。
?- a++b++c++d equ c++d++b++a.
true ;
false.
?- a++(b++c) equ (c++a)++b.
true ;
false.
?- a++(b++X) equ (c++Y)++b.
X = c,
Y = a ;
false.
?- (a++b)++X equ c++Y.
X = c,
Y = (a++b) ;
X = c,
Y = (b++a) ;
false.
eq/2 ルールでは
equ(A,B) :- sum_pp(A,AL,Len), sum_pp(B,BL,Len), !, permutation(AL, BL).
sum_pp への最初の呼び出しは長さを生成し、2 番目の呼び出しは長さを制約として受け取ります。最初の呼び出しが 2 番目のリストと一致することのない増え続けるリストを生成し続け、無限再帰につながる可能性があるため、カットが必要です。私はまだより良い解決策を見つけていません...
このような興味深い問題を投稿していただきありがとうございます。
/ピーター
編集: DCG ルールとして書かれた sum_pp_:
sum_pp(Term, List, Len) :- sum_pp_(Term, 0,Len, List, []).
sum_pp_(A, N0,N) --> { nonvar(A), A\=(_++_), N is N0+1 }, [A].
sum_pp_(A, N0,N) --> { var(A), N is N0+1 }, [A].
sum_pp_(A1++A2, N0,N) -->
sum_pp_(A1, N0,N1), { nonvar(N), N1>=N -> !,fail; true },
sum_pp_(A2, N1,N).
アップデート:
sum_pp(Term, List, Len) :-
( ground(Term)
-> sum_pp_chk(Term, 0,Len, List, []), ! % deterministic
; length(List, Len), Len>0,
sum_pp_gen(Term, 0,Len, List, [])
).
sum_pp_chk(A, N0,N) --> { A\=(_++_), N is N0+1 }, [A].
sum_pp_chk(A1++A2, N0,N) --> sum_pp_chk(A1, N0,N1), sum_pp_chk(A2, N1,N).
sum_pp_gen(A, N0,N) --> { nonvar(A), A\=(_++_), N is N0+1 }, [A].
sum_pp_gen(A, N0,N) --> { var(A), N is N0+1 }, [A].
sum_pp_gen(A1++A2, N0,N) -->
{ nonvar(N), N0+2>N -> !,fail; true }, sum_pp_gen(A1, N0,N1),
{ nonvar(N), N1+1>N -> !,fail; true }, sum_pp_gen(A2, N1,N).
sum_pp を 2 つのバリアントに分割します。1 つ目は、基本条件をチェックし、決定論的であるスリム バージョンです。2 番目のバリアントはlength/2
、右の再帰が何かを生成する機会を得る前に左の再帰が逃げるのを防ぐために、ある種の反復的な深化を実行するように呼び出します。各再帰呼び出しの前に長さチェックを行うことで、これは以前よりも多くのケースで無限再帰証明になります。
特に、次のクエリが機能するようになりました。
?- sum_pp(Y, L, N).
L = [Y],
N = 1 ;
Y = (_G1515++_G1518),
L = [_G1515,_G1518],
N = 2 .
?- sum_pp(Y, [a,b,c], N).
Y = (a++b++c),
N = 3 ;
Y = ((a++b)++c),
N = 3 ;
false.