This question is pretty old, but I will post my solution anyway. I study prologue in my free time and found this rather complex problem.
I learned a lot about DCG and difference lists. I'm afraid I did not come up with a solution that does not use lists. Like the proposed layout, it converts terms to flat lists to deal with parentheses, and uses permutation/2 to match lists, given the commutative nature of the ++ operator ...
Here is what I came up with:
:- 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).
The predicate sum_pp/3 is a workhorse that accepts the term and converts it into a flat list of terms, returning (or checking) the length, being immune to parentheses. It is very similar to the DCG rule (using difference lists), but it is written as a regular predicate because it uses length to help break left recursion, which would otherwise lead to infinite recursion (it took me quite a while to defeat it )
He can check ground conditions:
?- sum_pp(((a++b)++x++y)++c++d, L, N). L = [a,b,x,y,c,d], N = 6 ; false.
It also generates solutions:
?- 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 .
The equ/2 operator "merges" two members and can also provide solutions if there are variables:
?- 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.
In the equ / 2 rule
equ(A,B) :- sum_pp(A,AL,Len), sum_pp(B,BL,Len), !, permutation(AL, BL).
the first call to sum_pp generates a length, while the second call takes the length as a constraint. Reduction is necessary because the first call can continue to generate ever-growing lists that will never coincide with the second list, resulting in infinite recursion. I have not found a better solution ...
Thanks for posting such an interesting issue!
/ Peter
edit: sum_pp_, written as DCG rules:
sum_pp(Term, List, Len) :- sum_pp_(Term, 0,Len, List, []). sum_pp_(A, N0,N)
update:
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).
I divided sum_pp into two options. The first is a thin version that checks ground terms and is deterministic. The second option calls length/2 to do some iterative deepening to prevent the left recursion from disappearing before the right recurson gets a chance to create something. Together with length checks before each recursive call, this is now endless proof of recursion for many other cases than before.
In particular, the following queries are now executed:
?- 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.