claire
claire

Reputation: 39

for-loop in prolog doesn't work

I'm trying to do a for loop in Prolog which doesn't work. The program should do following:

generate(N,[S,E],FinalSegments):-
   segments([S,E],Initialsegments),
   iterate_level(N,Initialsegments,FinalSegments).

When I'm calling generate(5,[(10,0),(-10,0)]) the first step is, that generate(5,[(10,0),(-10,0)],FinalSegments) is being called and this predicate generates 4 coordinates between the Startpoint (10,0) and the Endpoint (-10,0), and store those four ccordinates in the listFinalSegments. This is actually done correctly. In the next step the predicate iterate_level(N,Initialsegments,FinalSegments) is being called. The predicate iterate_level(), takes the four coordinates from the last step as a list called Initialsegments:

Initialsegments:([[ (10, 0), (3.333333333333333, 0)],
                 [ (3.3333, 0), (-3.5527e-15, -5.7735)], 
                 [ (-3.5527e-15, -5.77350), (-3.3333, 0)],
                 [(-3.3333, 0), (-10, 0)]])

And now iterate_level(5,Initialsegments,FinalSegments) should be a for-loop which should generate 16 coordinates after the first iteration, then 64 coordinates after the second iteration... But here is my problem that this is not really working and I don't know what I'm still doing wrong. It seem to me, when I'm looking at the trace, that

iterate_level(N,Ls,F):-
   seq(0,N,Index),
   next_level_segments(Index,Ls,F).

when next_level_segments(Index,Ls,F) is called within the for-loop, the list Ls which should contain four times more coordinates after each iteration is not refreshed.(Maybe this is the problem). When I call generate(3,[[(60,0),(-60,0)]],X). I get four times always the same 16 coordinates as a result but I should get 1024 different coordinates. Maybe someone may have some time to have a look at this problem an give me some help. Thanks

This is my implementation until now:

generate(N,[S,E],FinalSegments):-
   segments([S,E],Initialsegments),
   iterate_level(N,Initialsegments,FinalSegments).
generate(N,[],[]).

seq(From,_,From).
seq(From,To,X) :-
   From<To,
   Next is From+1,
   seq(Next,To,X).

iterate_level(N,Ls,F):-
   seq(0,N,Index),
   next_level_segments(Index,Ls,F).
   %fail.

iterate_level(Ls,F).
iterate_level([],[]).

segments([(Sx,Sy),(Ex,Ey)],Ls):-
   X2 is Sx+(Ex-Sx)/3,
   Y2 is Sy+(Ey-Sy)/3,
   R1 is sqrt((X2-Sx)*(X2-Sx)+(Y2-Ey)*(Y2-Ey)),
   Phi1 is atan((Y2-Sy)/(X2-Sx)),
   X3 is X2 +R1*cos((Phi1-240)*pi/180),
   Y3 is Y2 +R1*sin((Phi1+240)*pi/180),
   X4 is X2+(X2-Sx),
   Y4 is Y2+(Y2-Sy),
   Ls=[
          [(Sx,Sy),(X2,Y2)],
          [(X2,Y2),(X3,Y3)],
          [(X3,Y3),(X4,Y4)],
          [(X4,Y4),(Ex,Ey)]

        ].    

next_level_segments(N,[[(Sx,Sy),(Ex,Ey)]|E],[X|RLs]):-
   segments([(Sx,Sy),(Ex,Ey)],X),
   next_level_segments(N,E,RLs). 
next_level_segments(N,[],[]).

Upvotes: 0

Views: 217

Answers (1)

Jim Ashworth
Jim Ashworth

Reputation: 765

So, first of all, Prolog doesn't do for-loops in the traditional sense - what you actually want to do is recurse over the list with an accumulator. This can be achieved as follows.

Firstly, generate(0, [[X|Y]|Z], [[X|Y]|Z]) :- !., which says "if I am trying to generate the 0th iteration of a list of lists, I have achieved my goal and I should succeed". This also cuts, as there is only ever going to be a single solution here.

generate(N, [[P1, P2]|Tail], Final) does the main body of the (outer) recursion. As long as this is a positive iteration (ie exclude negatives), we do_generate an iteration of coordinates, and recurse for another iteration (with iteration 0 succeeding as above).

do_generate([],[]). states that if we're trying to generate coordinates between an empty list, we're done for this level.

do_generate([Current|Rest], Interim) takes the first pair of coordinates and generates the set of four pairs of coordinates (using segments([(Sx,Sy),(Ex,Ey)],Ls) as before), then recurses onto the rest of the list of coordinates. Once we reach the above base-case, we append all the lists together from last to first to get the new set of coordinates. This is then unified with Interim, to send back to generate(N, [[P1, P2]|Tail], Final) for further recursion or unification with Final using the outer base-case.

As a caveat, in order to get output looking like input for the final base-case, the input is now required to be a list of lists of coordinate-pairs, not just a list of coordinate-pairs.

All put together, you get the following:

generate(0, [[X|Y]|Z], [[X|Y]|Z]) :- !.
generate(N, [[P1, P2]|Tail], Final) :-
    N > 0,
    do_generate([[P1, P2]|Tail], Interim),
    N1 is N-1,
    generate(N1, Interim, Final).

do_generate([], []).
do_generate([Current|Rest], Interim) :-
    segments(Current, Segs),
    do_generate(Rest, RestSegs),
    append(Segs, RestSegs, Interim).

segments([(Sx,Sy), (Ex,Ey)], Ls) :-
   X2 is Sx+(Ex-Sx)/3,
   Y2 is Sy+(Ey-Sy)/3,
   R1 is sqrt((X2-Sx)*(X2-Sx)+(Y2-Ey)*(Y2-Ey)),
   Phi1 is atan((Y2-Sy)/(X2-Sx)),
   X3 is X2+R1*cos((Phi1-240)*pi/180),
   Y3 is Y2+R1*sin((Phi1+240)*pi/180),
   X4 is X2+(X2-Sx),
   Y4 is Y2+(Y2-Sy),
   Ls=[[(Sx,Sy),(X2,Y2)],
       [(X2,Y2),(X3,Y3)],
       [(X3,Y3),(X4,Y4)],
       [(X4,Y4),(Ex,Ey)]].

Upvotes: 3

Related Questions