3

How to recognize A^n B^n language in Prolog without arithmetics and for any A, B where A != B?

With known A = a and B = b we could write

% For each 'a' save 'b' in a list, then check 
% whether constructed list is equal to the rest of input list

anbn(L) :- anbn(L, []). 

anbn(L, L). 

anbn([a|L],A) :- anbn(L, [b|A]).

For any A and B I was thinking of a solution starting with

anbn(L) :- anbn(L, []).

anbn([H|L],[]) :- anbn(L,[H]). % save an element

anbn([H|L], [H|A]) :- anbn(L, [H,H|A]). % make sure front elements are the same

so that the first elements are all the same, but than I don't see an elegant way of checking whether all elements in the rest of the list are the same and different than the elemenets in the front.

I could check whether the rest is as long as the stored list and then whether it only consists of the second type elements but I believe I'm overcomplicating the problem and there exists a short and simple solution.

false
  • 10,264
  • 13
  • 101
  • 209
Legat
  • 1,379
  • 3
  • 11
  • 20
  • 1
    Why aren't you using definite clause grammars? – Fred Foo May 07 '13 at 11:34
  • @larsmans I tried a solution with a DCG but the requirements of arbitrary A's and B's and no arithmetic made the solution not much better than normal notation. Can you please show your solution? –  May 07 '13 at 11:45
  • I may be wrong, but a^n b^n it's not a *regular* language. – CapelliC May 07 '13 at 11:46
  • @Boris: `s --> []. s --> [a], s, [b].` then add two arguments to `s` and make two minor changes to the second rule. (I'm trying not to give away the answer.) – Fred Foo May 07 '13 at 12:01
  • @CapelliC: no, it's not, why? – Fred Foo May 07 '13 at 12:08
  • @larsmans: I was referring to the tag automata, seems inappropriate. – CapelliC May 07 '13 at 12:11
  • @CapelliC: aⁿbⁿ is context-free, so it can be recognized by a push-down automaton. – Fred Foo May 07 '13 at 12:14
  • @larsmans Never heard about DCG until now but still, I wanted to solve problem myself - not hand it to some magical blackbox :) – Legat May 07 '13 at 22:01

5 Answers5

8

Use a definite clause grammar.

s(_, _) --> [].
s(A, B) --> [A], s(A, B), [B].

Demo:

?- phrase(s(1, 2), X).
X = [] ;
X = [1, 2] ;
X = [1, 1, 2, 2] ;
X = [1, 1, 1, 2, 2, 2] .
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 2
    Very nice, +1. In contrast to other solutions posted here, yours also works correctly for the most general query, `?- phrase(s(X,Y), Ls).`. Notice though that you do not yet satisfy OP's requirement that `X` and `Y` be distinct. Use `dif/2` in the first rule. One should always use the `phrase/2` (or `phrase/3`) interface to DCGs because the translation of DCGs to Prolog predicates may change depending on the Prolog implementation or version. – mat May 07 '13 at 15:16
  • 2
    The only solution that doesn't burn my eyes. :) +1 – Daniel Lyons May 07 '13 at 16:34
  • @mat: I read the A!=B as a requirement that the solution work in that case, not that it is actually enforced. I removed the usage example without `phrase/2`, though. – Fred Foo May 07 '13 at 16:47
  • @mat: Also, I just tried your suggestion of putting a `dif` in the first rule and that backtracks forever on `phrase(s(1,1),S)`. The constraint should really be handled outside of the grammar/recursion. – Fred Foo May 07 '13 at 16:49
  • @mat "works correctly ... do not yet satisfy " that's a self-contradiction. "most general query `phrase(s(X,Y), Ls)` with an uninstantiated `Ls` is contradictory to the stated requirement, `X\=Y`. With instantiated `Ls`, my (and probably others') answer works correctly. – Will Ness May 07 '13 at 17:57
  • @larsmans the requirement is "to recognize `A^nB^n for any A, B where A != B`". That means that the case where `A=B` must be rejected. your answer doesn't reject such case. (it is easy to amend it so that it does, of course, although it might not be easy for the OP). – Will Ness May 07 '13 at 18:00
  • @WillNess I'm afraid lars still has the trophy. :) – Daniel Lyons May 07 '13 at 18:40
  • @larsmans: For this I would simply add `dif/2` also to the second rule. – mat May 07 '13 at 21:13
  • @larsmans That's a nice and short solution even though it just delegates the problem :p Anyway, how to add the dif/2 here? if I just put it like this `s(A, B) --> [A], s(A, B), [B], dif(A,B).` I get error `Existence error in user:dif/4` for `phrase(s(1, 2), X).` – Legat May 07 '13 at 22:14
  • @Legat: you might write a helper predicate, e.g. `anbn(A,B,S) :- dif(A,B), phrase(s(A,B), S).` – Fred Foo May 07 '13 at 22:28
2

Edit: back to the original solution, and sticking to it:

anbn(List) :- List = [] -> true; List = [A|Rest], a(Rest, A, 0).

a([A|Rest], A, N) :- !, a(Rest, A, s(N)).
a([B|Rest], _, N) :- b(Rest, B, N).

b([B|Rest], B, s(N)) :- b(Rest, B, N).
b([], _, 0).

It is iterative, it does not create choice-points, it is obvious, and it is correct, if all elements of the list are ground.

2

It is common for DCGs that naked variables are rewrapped as phrase/3 during translation. So that one can implement A^n B^n not only for when A and B are terminals, but also when A and B are arbitrary DCGs goals.

Here is the code for that:

 s(_,_) --> [].
 s(A,B) --> A, s(A,B), B.

Here one sees the translation that is done by SWI-Prolog. As one can see the naked variables have been converted to phrase/3 goals:

 ?- listing.
 s(_, _, A, A).
 s(A, C, B, F) :-
    phrase(A, B, D),
    s(A, C, D, E),
    phrase(C, E, F).

Here is a sample run, for terminals A and B:

 ?- phrase(s("alfa","beta"),X), atom_codes(Y,X).
 X = [],
 Y = '' ;
 X = [97, 108, 102, 97, 98, 101, 116, 97],
 Y = alfabeta ;
 X = [97, 108, 102, 97, 97, 108, 102, 97, 98|...],
 Y = alfaalfabetabeta ;
 X = [97, 108, 102, 97, 97, 108, 102, 97, 97|...],
 Y = alfaalfaalfabetabetabeta .

Here is a sample run, for some DCG goals as A and B:

  bit --> "0".
  bit --> "1".

  ?- length(L,8), phrase(s(("(",bit),(bit,")")),L), atom_codes(R,L).
  L = [40, 48, 40, 48, 48, 41, 48, 41],
  R = '(0(00)0)' ;
  L = [40, 48, 40, 48, 48, 41, 49, 41],
  R = '(0(00)1)' ;
  L = [40, 48, 40, 48, 49, 41, 48, 41],
  R = '(0(01)0)' ;
  Etc..

Bye

0

I think it's simpler than that:

anbn(L) :- append(As, Bs, L), maplist(ab, As, Bs).
ab(a, b).

EDIT: This is easily generalized to arbitrary literals.

anbn(L) :- L = [A|_], append(As, Bs, L), maplist(ab(A), As, Bs).
ab(A, A, B) :- A \== B.

EDIT: after Will Ness noting that's bugged: what I meant was

anbn(L) :- append([A|As], [B|Bs], L), A \= B, maplist(ab(A, B), As, Bs).
ab(A, B, A, B).
CapelliC
  • 59,646
  • 5
  • 47
  • 90
0
anbn( [] ) :- !.
anbn( L ) :- L=[A|_], copy_half( L,L, Z,Z, A,_V). % Z is second half
copy_half( [A|B], [_,_|C], [V|D], Z, A,V) :- !, copy_half( B,C, D,Z, A,V).
copy_half( Z,     [],      [],    Z, A,V) :- A \== V.

Equivalent to

anbn_verboten(L):- L = [] ;
  length(L,N), N2 is N div 2, length(X,N2), length(Y,N2), append(X,Y,L), 
  L=[P|_], Y=[Q|_], P \= Q.`.
Will Ness
  • 70,110
  • 9
  • 98
  • 181