SWI Prolog if statements, how do they work? Generating a simple grid

I realize I've edited out the if statements out of the original code which doesn't help readability and question clarity. Just skip to the answers for the explanation on how they work with a small example program.

To learn about more complex programs using if statements in Prolog, I'm creating a simple platformer that generates some objects and places them in a grid. First I'm trying to generate a simple 'world' with the idea of trying out generating things in prolog. The plan is to create a grid of 50 lists with 10000 items, which really shouldn't be that complicated but I can't get the if statements to work as I get the impression that I'm fundamentally misunderstanding how they work vs how I think they work. What happens is the condition isn't met, the if statement isn't called but the whole predicate is recalled with empty variables and evaluations are not instantiated.

  • Create a simple accumulator which has an X and Y axis, and limits to how far they go before failing the predicate.
  • If the number of Y rows has been reached, terminate
  • Create a new [id, point(X,Y), Image] to be later filled with something
  • If X = end of the row, X is 0, else create the next point
  • Code:

    generate(WorldList) :- generate_world(WorldList,0,_,10000,0,_,50).
    
    generate_world([H|T],X,_,XEnd,Y,_,YEnd) :-
        %Y has been filled with 50 rows, end recursion
        not(Y > YEnd),
        %iterate X by 1, store in XNew
        XNew is X + 1,  
        %create a new [id,point(X,Y), Image]
        H = [XNew,point(_,_)],
        %if X has reached 10k, add 1 to Y and create a new row
        X = XEnd -> YNew is Y + 1, 
        generate_world(T,0,_,XEnd,YNew,_,YEnd);
        %continue adding items to current row Y
        generate_world(T,XNew,_,XEnd,Y,_,YEnd).
    generate_world([],_,_,_,_,_,_).
    

    Am I doing something blatantly wrong or how are you supposed to use prolog conditional statements and can they even be used like this at all?

    The way I expect it to work is a term is evaluated, then do what is to the left of the following OR if it's true, or the right if it's false. That happens, but I don't understand why the entire predicate is called again as it also empties the variables being evaluated. My brain hurts.

    What the docs say: http://www.swi-prolog.org/pldoc/man?predicate=-%3E/2

    @damianodamiano identified the problem, if statements in prolog need to be surrounded by () tags. I'd still like a more detailed explanation of how they actually work in regards to choice points, backtracking and other Prolog specific things I might not even know about.


    Your predicate stops as soon as you run it because in not(By > YEnd) , By is not instantiated (note that By is also a singleton variable and each singleton variable is useless and can drive to errors). Here i post two implementation, the first without if statement (which personally prefer), the second with if statement (i've put 2 and 2 as bound for brevity...).

    First implementation:

    generateList(L):-
        generateWL(L,0,2,0,2).
    generateWL([],0,_,Y,Y). %you can add a ! here
    generateWL(L,MaxX,MaxX,R,MaxR):- %you can add a ! here
        R1 is R+1,
        generateWL(L,0,MaxX,R1,MaxR).
    generateWL([H|T],X,MaxX,R,MaxR):-
        X < MaxX,
        R < MaxR,
        X1 is X+1,
        H = [X1,point(X1,R)],
        generateWL(T,X1,MaxX,R,MaxR).
    
    ?- generateList(WL).
    WL = [[1, point(1, 0)], [2, point(2, 0)], [1, point(1, 1)], [2, point(2, 1)]]
    false
    

    If you want to prevent backtracking, just add the two cuts i've annotated.

    Second implementation

    generateList2(L):-
        generateWLIf(L,0,2,0,2).
    
    generateWLIf([H|T],X,MaxX,R,MaxR):-
        (   X < MaxX, R < MaxR ->    
            X1 is X+1,
            H = [X1,point(X1,R)],
            generateWL(T,X1,MaxX,R,MaxR)
        ;   X = MaxX, R < MaxR ->  
            R1 is R+1,
            generateWL([H|T],0,MaxX,R1,MaxR)
        ;   R = MaxR ->  T = []).
    
    ?- generateList2(WL).
    WL = [[1, point(1, 0)], [2, point(2, 0)], [1, point(1, 1)], [2, point(2, 1)]]
    

    (Continuing from the comments)

    The way I expect [conditional statements] to work is a term is evaluated, then do what is to the left of the following OR if it's true, or the right if it's false. That happens, but I don't understand why the entire predicate is called again as it also empties the variables being evaluated.

    You probably mean that it back-tracks, and the reason is that the comparison not(Y > YEnd) eventually fails, and there is no else-clause (and no if either).

    Also, your base case makes no sense, as the list is output not input. And you want to compare against XNew not X .

    generate(WorldList) :-
      generate_world(WorldList,1,10000,1,50).
    
    generate_world(T,X,XEnd,Y,YEnd) :-
      ( Y = YEnd ->
        T = []
      ; T = [point(X,Y)|Rest], XNew is X + 1,
        ( XNew = XEnd -> YNew is Y + 1,
          generate_world(Rest,1,XEnd,YNew,YEnd)
        ; generate_world(Rest,XNew,XEnd,Y,YEnd) ) ).
    

    This would seem to work in the sense that it does what you describe, but it is not good design. Now you have to pass this enormous list around all the time, and updating one location means deconstructing the list.

    Your problem:

    I'm creating a simple platformer that generates some objects and places them in a grid. First I'm trying to generate a simple 'world' with the idea of trying out generating things in prolog. The plan is to create a grid of 50 lists with 10000 items

    is much better solved in Prolog by having a predicate location/3 (for example) where the parameters are the coordinates and the content.

    location(1,1,something).
    location(1,2,something).
    location(1,3,somethingelse).
    ...
    

    And this predicate is created dynamically, using assert/3 .


    This is based on my understanding of ISO-prolog and the other answers given, boiled down to the essence of how if then else works in Prolog.

    The if predicate -> forces evaluation its the surrounding complex terms grouped by ( and ) . The outer brackets identify the if-statement as ( if -> then ; else ) , where if , then and else are each goals in the form of terms to be evaluated, which return yes or no , also grouped by ( and ) . Whether then or else is called, separated by the OR operator ; , depends on the yes or no result from the evaluated term represented by if . The outer groupings are strictly necessary while the inner ones are optional, BUT it's good practice in my opinion to add them anyway, given that you can nest another if statement as a term surrounded by () in the result of the first, which likely produces unwanted result and makes the code much harder to read, and any non-grouped nested ; will identify the right side as the else .

    Choice points are created where there are variables that can have multiple possible answers as a possible solution to the posed goal. This means within an if , if a term can be satisfied in multiple ways, Prolog will try to satisfy that goal as a separate goal and then use the result to determine the outcome of the surrounding term. If a goal fails, it behaves like normal code and doesn't try to satisfy the goals further right.

    If a choice point is before the whole if statement section, the whole section will be checked again.

    Example program to clarify the idea.

    fact(a).
    fact(f).
    
    start :-
            (
                %The entire complex term is evaluated as yes
                (fact(a), write('first if'), nl) -> 
                    %triggers the first line
                    (write('first then'),nl) ; 
                    (write('first else'),nl)        
            ),
            (
                %The entire complex term is evaluated as no
                (fact(B), write('second if'), B = b, nl) -> 
                    (write('second then'),nl) ; 
                    %triggers the second line
                    (write('second else'),nl)
            ).
    

    And the output for ?- start.

    first if
    first then
    second ifsecond ifsecond else
    
    链接地址: http://www.djcxy.com/p/66950.html

    上一篇: 如何在c#中散列int []

    下一篇: SWI Prolog if语句,它们是如何工作的? 生成一个简单的网格