This question is rather old, but I'll post my solution anyways. I'm learning prolog in my spare time, and found this quite a challenging problem.
I learned a lot about DCG and difference lists. I'm afraid, I didn't come up with a solution that does not use lists. Like mat suggested, it transforms terms into flat lists to cope with the parentheses, and uses permutation/2
to match the lists, accounting for the commutative nature of the ++ operator...
Here's 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 sum_pp/3
predicate is the workhorse which takes a term and transforms it into a flat list of summands, returning (or checking) the length, while being immune to parentheses. It is very similar to a DCG rule (using difference lists), but it is written as a regular predicate because it uses the length to help break the left recursion which would otherwise lead to infinite recursion (took me quite a while to beat it).
It can check ground terms:
?- 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 "unifies" two terms, 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. The cut is necessary, because the first call may continue to generate ever growing lists that will never again match with the second list, leading to infinite recursion. I haven't found a better solution yet...
Thanks for posting such an interesting problem!
/Peter
edit: sum_pp_ written as DCG rules:
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).
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 split sum_pp into two variants. The first is a slim version that checks ground terms and is deterministic. The second variant calls length/2
to perform some kind of iterative deepening, to prevent the left-recursion from running away before the right recurson gets a chance to produce something. Together with the length checks before each recursive call, this is now infinite recursion proof for many more cases than before.
In particular the following queries now work:
?- 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.