Linear implication is a serious and very convenient problem solving tool, which allows avoiding explicit handling of complex data-structures. Let's suppose we want to walk through a (possibly circular) graph structure without looping.
With linear implication this becomes:
path(X,X,[X]). path(X,Z,[X|Xs]):-linked(X,Y),path(Y,Z,Xs). linked(X,Y):-c(X,Ys),member(Y,Ys). go(Xs):- c(1,[2,3]) -: c(2,[1,4]) -: c(3,[1,5]) -: c(4,[1,5]) -: path(1,5,Xs).
path(X,X,[X]). path(X,Z,[X|Xs]):-c(X,Y),path(Y,Z,Xs). % data go(Xs):- (c(1,X1):-c1(X1)) -: (c(2,X2):-c2(X2)) -: (c(3,X3):-c3(X3)) -: (c(4,X4):-c4(X4)) -: path(1,5,Xs). c1(2). c1(3). c2(1). c2(4).
Some finite domain constraint solving can also be done quite efficiently (1.3 seconds on a Sparc 10-20 for the SEND + MORE = MONEY puzzle - see file progs/lconstr.pl).
% Program: linear implication based FD constraint solving % Author: Paul Tarau, 1995 % cryptarithmetic puzzle solver -see progs/lconstr.pl % a kind of "constraint solving with linear implication" example1( [s,e,n,d,m,o,r,e,y]=[S,E,N,D,M,O,R,E,Y], [S,E,N,D]+ [M,O,R,E]= [M,O,N,E,Y], _ ). % Addition of two numbers - simplified version - sum(As, Bs, Cs) :- sum(As, Bs, 0, Cs). sum([], [], Carry, [Carry]). sum([A|As], [B|Bs], Carry, [C|Cs]) :- !, add2digits(A,B,Carry,C,NewCarry), sum(As, Bs, NewCarry, Cs). add2digits(A,B,Carry,Result,NewCarry):- bind(A),bind(B), add_with_carry(10,A,B,Carry,Result,NewCarry). add_with_carry(Base,A,B,Carry,Result,NewCarry):- S is A+B+Carry, Result is S mod Base, NewCarry is S // Base, new_digit(Result). bind(A):-var(A),!,digit(A). bind(_). new_digit(A):-digit(A),!. new_digit(_). solve(As,Bs,Cs,Z):- digit(0)-:digit(1)-:digit(2)-:digit(3)-:digit(4)-:digit(5)-: digit(6)-:digit(7)-:digit(8)-:digit(9)-: ( sum(As,Bs,Cs), Z>0 ). puzzle:- init(Vars,Puzzle,Names), Puzzle=(Xs+Ys=Zs),Zs=[Z|_], reverse(Xs,As), % see progs/lconstr.pl reverse(Ys,Bs), reverse(Zs,Cs), solve(As,Bs,Cs,Z), show_answer(Vars,Names,Puzzle), % see progs/lconstr.pl fail. puzzle:- write('no (more) answers'),nl,nl. go:- (init(X,Y,Z):-example1(X,Y,Z))-:puzzle, fail.
Notice how linearly assumed digit/1
facts are consumed
by bind/1
and new_digit/1
to enforce constraints as early
as possible inside the addition loop.