SWI: полудетерминированное поведение: случайное/3 в контексте вызова/1?

Это было мое первое столкновение с понятием детерминированности в Прологе: noredirect=1#comment84840684_48916791">Почему Prolog не выполняет возврат при сравнении?

Я также нашел это обсуждение интересным: Имеет ли понятие "semidet" в Prolog улажено?

Я хотел реализовать более общую конструкцию цикла (которая ведет себя как findall): (Кстати: источник findall кажется странным, не могу понять, что это за «$....»).

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).

Как видите, это работает в общем случае:

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

И снова останавливаемся на random/3 :

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

Я ожидал, что поведение samidet будет сохраняться только в текущем контексте вызова, а не при рекурсивном вызове? (Кстати, swi-docs говорят, что random/3 — это det, а random_between/3 — semidet. Оба терпят неудачу одинаково).

С другой стороны, если я делаю случайный выбор напрямую, а не через call/1, он работает!!!

Это код, из которого я решил абстрагироваться от «цикла» (играя с 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).

увидеть, как это работает:

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

Почему random/3 не работает в контексте call/1? Но работает как прямой вызов?

Кстати, я случайно наткнулся на random/3, есть ли другие предикаты, которые будут вести себя как random/3?


Согласно Таку:

 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].

person sten    schedule 26.02.2018    source источник


Ответы (1)


Это потому, что как только R из random(1,10,R) становится заземленным (привязанным к значению), R больше не может быть изменен. При рекурсивном вызове части loop(NI, Goal, [Item|Acc], RV) Goal фактически сопоставляется с образцом random(1,10,9), а 9 не может быть изменено.

вы должны внести это 9 изменение в свободную переменную.

этот код будет работать так, как вы предполагали.



    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].

person Taku Koyahata    schedule 27.02.2018