Search code examples
loopsprologcalldeterministic

SWI: Semideterminate behavior : random/3 in call/1 context?


This was my initial bump-up with the notion of determinacy in Prolog : Why does Prolog does not backtrack on comparison?

I also found this discussion interesting : Has the notion of 'semidet' in Prolog settled?

I wanted to implement more general loop construct (that behave like findall) : (BTW: findall source seem weird, cant understand what this '$....' is all about).

fact(1,2).

loop(0,_,RV,RV) :- !.
loop(I, Goal, Acc, RV) :- I > 0, NI is I - 1, call(Goal), Goal =.. Lst, last(Lst,Item),  writeln(Item), loop(NI, Goal, [Item|Acc], RV).

As you can see it works in the general case :

?- loop(3,fact(1,R), [], RV).
2
2
2
R = 2,
RV = [2, 2, 2].

And again balks-up on random/3 :

?- loop(3,random(1,10,R), [], RV).
9
false.

I expected samidet behavior to hold only in the current call-context not when called recursively ? (BTW, swi-docs say that random/3 is det and random_between/3 is semidet. Both fail in the same way).

On the other hand if I do random directly rather than via call/1 it works !!!

This is the code from which I decided to abstract 'loop' (playing with ugraph lib) :

rand_edges(0, _, _, E, E) :- !.
rand_edges(I, RF, RT, E, RV) :- I > 0, NI is I - 1, random(RF,RT,R1), random(RF,RT,R2), rand_edges(NI, RF, RT, [R1-R2|E], RV).
rand_edges(I, RangeFrom, RangeTo,  Edges) :- rand_edges(I, RangeFrom, RangeTo, [], Edges).
rand_edges(I, Edges) :- rand_edges(I, 1, 10, [], Edges).

see it working :

?- rand_edges(5,E).
E = [5-5, 9-7, 2-2, 2-7, 3-5].

Why random/3 doesn't work in call/1 context ? But works as direct call ?

BTW, I just happen to stumble upon random/3, is there other predicates that will behave like random/3 ?


As per Taku :

 loop(0,_,RV,RV) :- !.
 loop(I, Goal, Acc, RV) :- 
     I > 0, NI is I - 1, call(Goal), Goal =.. Lst,
     %extract the result of the last call in Item, then substitute the last Var with new un-unified Var
     reverse(Lst,[Item|T]), reverse([_NewVar|T], NewLst),
     NewGoal =.. NewLst, %build a goal with the new-Var
     loop(NI, NewGoal, [Item|Acc], RV).
 loop(I, Goal, RV) :- loop(I, Goal, [], RV).



 ?- loop(5, random(1,10,_R), RV).
 _R = 7,
 RV = [4, 9, 8, 2, 7].

Solution

  • It's because once R of random(1,10,R) becomes ground(bound to a value), no longer R cannot be changed. At recursion call of loop(NI, Goal, [Item|Acc], RV) part , Goal is patternmatched to random(1,10,9) actually and 9 cannnot be changed.

    you should make this 9 change to free variable.

    this code will work as you intended.

    
    
        loop(0,_,RV,RV) :- !.
        loop(I, Goal, Acc, RV) :- 
        I > 0, 
        NI is I - 1, 
        call(Goal), 
        Goal =.. Lst, 
        last(Lst,Item),  
        writeln(Item), 
        nth1(1, Lst, First),
        nth1(2,Lst,Second),
        nth1(3,Lst,Third),
        Goal2 =.. [First,Second,Third,NewRand],
        loop(NI, Goal2, [Item|Acc], RV).
    
        ?- loop(5,random(1,10,R),[],RV).
        7
        9
        2
        4
        2
        R = 7,
        RV = [2, 4, 2, 9, 7].