220 likes | 364 Views
Declarative Loops and List Comprehensions for Prolog. Neng-Fa Zhou Brooklyn College The City University of New York zhou@sci.brooklyn.cuny.edu. Motivation. Second ASP-solver Competition 38 problems were used The B-Prolog team was the only CLP(FD) team
E N D
Declarative Loops and List Comprehensions for Prolog Neng-Fa Zhou Brooklyn College The City University of New York zhou@sci.brooklyn.cuny.edu
Motivation • Second ASP-solver Competition • 38 problems were used • The B-Prolog team was the only CLP(FD) team • We had to develop solutions from scratch • We desperately needed loop constructs
One of the Problems:Maze generation • Conditions • 1. There must be a path from the entrance to every empty cell. • 2. There must be no 2x2 blocks of empty cells or walls. • 3. No wall can be completely surrounded by empty cells. • …
The Array Subscript Notation for Structures and Lists in B-Prolog • In arithmetic expressions • In arithmetic constraints • In calls to @= and @:= • In any other context, X[I1,…,In] is the same as X^[I1,…,In] S is X[1]+X[2]+X[3] X[1]+X[2] #= X[3] X[1,2] @= 100X[1,2] @:= 100
foreach(E1inD1, . . ., EninDn, Goal) • EiinDi • Ei is a pattern (usually a variable but can be a compound term) • Di is a collection (a list or a range of integers l..u) • Semantics • For each combination of values E1D1, . . ., EnDn, execute Goal
Example-1 • Using foreach • Using recursion ?- L=[1,2,3],foreach(X in L, write(X)). ?- L=[1,2,3],write_list(L). write_list([]). write_list([X|T]) :- write(X), write_list(T).
Example-2 • Using foreach • Using recursion ?- foreach(X in 1..3, write(X)). ?- write_int_range(1,3). write_int_range(I,N):-I>N,!. write_int_range(I,N) :- write(I), I1 is I+1, write_int_range(I1,N).
Example-3 (compound patterns) • Using foreach • Using recursion (matching clauses) ?-L=[(a,1),(b,2)],foreach((A,I) in L, writeln(A=I)). ?- L=[(a,1),(b,2)],write_pairs(L). write_pairs([]) => true. write_pairs([(A,I)|L]) => writeln(A=I), write_pairs(L).
foreach(E1inD1, . . ., EninDn, LVars, Goal) Variables in LVars are local to each iteration. • Using foreach • Using recursion ?-S=f(1,2,3), foreach(I in 1..S^lengh,[E], (E @= S[I], write(E))). ?-S=f(1,2,3),functor(S,_,N),write_args(S,1,N).write_args(S,I,N):-I>N,!.write_args(S,I,N):- arg(I,S,E), write(E), I1 is I+1, write_args(S,I1,N).
List Comprehension [T : E1inD1, . . ., EninDn, LVars, Goal] • Calls to @=/2 • Arithmetic constraints ?- L @= [X : X in 1..5].L=[1,2,3,4,5]?-L @= [(A,I): A in [a,b], I in 1..2].L= [(a,1),(a,2),(b,1),(b,2)] sum([A[I,J] : I in 1..N, J in 1..N]) #= N*N
foreach With Accumulators L @= [(A,I): A in [a,b], I in 1..2] foreach(A in [a,b], I in 1..2, ac1(L,[]), L^0=[(A,I)|L^1])
Application Examples • Quick sort • Permutations • N-Queens • Non-boolean constraints • Boolean constraints • Gaussian elimination • No-Three-in-a-Line Problem • Maze generation
Quick Sort qsort([],[]). qsort([H|T],S):- L1 @= [X : X in T, X<H], L2 @= [X : X in T, X>=H], qsort(L1,S1), qsort(L2,S2), append(S1,[H|S2],S).
Permutations perms([],[[]]). perms([X|Xs],Ps):- perms(Xs,Ps1), Ps @= [P : P1 in Ps1, I in 0..Xs^length,[P], insert(X,I,P1,P)]. insert(X,0,L,[X|L]). insert(X,I,[Y|L1],[Y|L]):- I>0, I1 is I-1, insert(X,I1,L1,L).
The N-Queens Problem Qi: the number of the row for the ith queen. queens(N):- length(Qs,N), Qs :: 1..N, foreach(I in 1..N-1, J in I+1..N, (Qs[I] #\= Qs[J], abs(Qs[I]-Qs[J]) #\= J-I)), labeling([ff],Qs), writeln(Qs).
The N-Queens Problem (Boolean Constraints) Qij=1 iff the cell at (i,j) has a queen. bqueens(N):- new_array(Qs,[N,N]), Vars @= [Qs[I,J] : I in 1..N, J in 1..N], Vars :: 0..1, foreach(I in 1..N, sum([Qs[I,J] : J in 1..N]) #= 1), foreach(J in 1..N, sum([Qs[I,J] : I in 1..N]) #= 1), foreach(K in 1-N..N-1, sum([Qs[I,J] : I in 1..N, J in 1..N, I-J=:=K]) #=< 1), foreach(K in 2..2*N, sum([Qs[I,J] : I in 1..N, J in 1..N, I+J=:=K]) #=< 1), labeling(Vars).
Gaussian Elimination triangle_matrix(Matrix):- foreach(I in 1..Matrix^length-1, [Row,J], (select_nonzero_row(I,I,Matrix,J)-> (I\==J-> (Row @= Matrix[I], Matrix[I] @:= Matrix[J], Matrix[J] @:= Row) ; true ), foreach(K in I+1..Matrix^length, trans_row(I,K,Matrix)) ; true ) ).
No-Three-in-a-Line Problem no3_build(N):- new_array(Board,[N,N]), Vars @= [Board[I,J] : I in 1..N, J in 1..N], Vars :: 0..1, Sum #= sum(Vars), Sum #=< 2*N, foreach(X1 in 1..N, Y1 in 1..N, [L,SL], (L @= [Slope : X2 in 1..N, Y2 in 1..N, [Slope], (X2\==X1, Slope is (Y2-Y1)/(X2-X1))], sort(L,SL), % eliminate duplicates foreach(Slope in SL, sum([Board[X,Y] : X in 1..N, Y in 1..N, (X\==X1,Slope=:=(Y-Y1)/(X-X1))]) #< 3))), foreach(X in 1..N, sum([Board[X,Y] : Y in 1..N])#<3), labeling([Sum|Vars]), outputBoard(Board,Sum,N).
Maze generation % There must be no 2x2 blocks of empty cells or walls foreach(I in 1..M-1, J in 1..N-1, (Maze[I,J]+Maze[I,J+1]+Maze[I+1,J]+Maze[I+1,J+1]#>0, Maze[I,J]+Maze[I,J+1]+Maze[I+1,J]+Maze[I+1,J+1]#<4)),
Demo • B-Prolog version 7.4 • N-queens problem • www.probp.com/examples.htm
Conclusion • Foreach and list comprehension constitute a better alternative for writing loops • Recursion • Failure-driven loops • Higher-order predicates (e.g., findall,maplist) • The do construct in ECLiPSe Prolog • Very useful when used on arrays • Significantly enhance the modeling power of CLP(FD)