Prolog 如何冻结变量列表的目标?

Prolog 如何冻结变量列表的目标?,prolog,clpfd,automaton,prolog-coroutining,Prolog,Clpfd,Automaton,Prolog Coroutining,我的最终目标是制作一个具体化版本的automaton/3,如果传递给它的序列中有任何变量,它就会冻结。i、 我不希望自动机实例化变量 (fd_长度/3,如果_/3等由此处的其他人定义) 首先,我有一个针对单变量的具体化测试: var_t(X,T):- var(X) -> T=true; T=false. 这使我能够实施: if_var_freeze(X,Goal):- if_(var_t(X),freeze(X,Goal),Goal). 所以我可以这样做: ?-X=bo

我的最终目标是制作一个具体化版本的automaton/3,如果传递给它的序列中有任何变量,它就会冻结。i、 我不希望自动机实例化变量

(fd_长度/3,如果_/3等由此处的其他人定义)

首先,我有一个针对单变量的具体化测试:

var_t(X,T):-
  var(X) ->
  T=true;
  T=false.
这使我能够实施:

if_var_freeze(X,Goal):-
  if_(var_t(X),freeze(X,Goal),Goal).
所以我可以这样做:

?-X=bob,Goal =format("hello ~w\n",[X]),if_var_freeze(X,Goal).
其行为与:

?-Goal =format("hello ~w\n",[X]),if_var_freeze(X,Goal),X=bob.
当所有变量都已实例化时,如何将其展开以处理变量列表,从而只调用一次目标?

在这种方法中,如果我有多个变量,我可以得到我不想要的行为:

?-List=[X,Y],Goal = format("hello, ~w and ~w\n",List),
if_var_freeze(X,Goal),
if_var_freeze(Y,Goal),X=bob.

hello, bob and _G3322
List = [bob, Y],
X = bob,
Goal = format("hello, ~w and ~w\n", [bob, Y]),
freeze(Y, format("hello, ~w and ~w\n", [bob, Y])).
我试过:

freeze_list(List,Goal):-
  freeze_list_h(List,Goal,FrozenList),
  call(FrozenList).

freeze_list_h([X],Goal,freeze(X,Goal)).
freeze_list_h(List,Goal,freeze(H,Frozen)):-
  List=[H|T],
  freeze_list_h(T,Goal,Frozen).
其工作原理如下:

 ?- X=bob,freeze_list([X,Y,Z],format("Hello ~w, ~w and ~w\n",[X,Y,Z])),Y=fred.
 X = bob,
 Y = fred,
 freeze(Z, format("Hello ~w, ~w and ~w\n", [bob, fred, Z])) .

?- X=bob,freeze_list([X,Y,Z],format("Hello ~w, ~w and ~w\n",[X,Y,Z])),Y=fred,Z=sue.
Hello bob, fred and sue
X = bob,
Y = fred,
Z = sue .
这似乎没问题,但我在将其应用于automaton/3时遇到了问题。 重申其目的是制作一个具体化版本的automaton/3,如果传递给它的序列中有任何变量,它就会冻结。i、 我不希望自动机实例化变量

这就是我所拥有的:

ga(Seq,G) :-
    G=automaton(Seq, [source(a),sink(c)],
                     [arc(a,0,a), arc(a,1,b),
                      arc(b,0,a), arc(b,1,c),
                      arc(c,0,c), arc(c,1,c)]).

max_seq_automaton_t(Max,Seq,A,T):-
  Max #>=L,
  fd_length(Seq,L),
  maplist(var_t,Seq,Var_T_List), %find var_t for each member of seq
  maplist(=(false),Var_T_List),  %check that all are false i.e no  uninstaninated vars
  call(A),!,
  T=true.
max_seq_automaton_t(Max,Seq,A,T):-
  Max #>=L,
  fd_length(Seq,L),
  maplist(var_t,Seq,Var_T_List), %find var_t for each member of seq
  maplist(=(false),Var_T_List),  %check that all are false i.e no uninstaninated vars
  \+call(A),!,
  T=false.
max_seq_automaton_t(Max,Seq,A,true):-
  Max #>=L,
  fd_length(Seq,L),
  maplist(var_t,Seq,Var_T_List), %find var_t for each
  memberd_t(true,Var_T_List,true), %at least one var
    freeze_list_h(Seq,A,FrozenList),
  call(FrozenList),
  call(A).
max_seq_automaton_t(Max,Seq,A,false):-
  Max #>=L,
  fd_length(Seq,L),
  maplist(var_t,Seq,Var_T_List), %find var_t for each
  memberd_t(true,Var_T_List,true), %at least one var
    freeze_list_h(Seq,A,FrozenList),
    call(FrozenList),
  \+call(A).
这不起作用,在实例化X之前,应冻结以下目标:

?- Seq=[X,1],ga(Seq,A),max_seq_automaton_t(3,Seq,A,T).
Seq = [1, 1],
X = 1,
A = automaton([1, 1], [source(a), sink(c)], [arc(a, 0, a), arc(a, 1, b), arc(b, 0, a), arc(b, 1, c), arc(c, 0, c), arc(c, 1, c)]),
T = true 
更新这是我现在所拥有的,我认为这是我最初想要的,但我正在消化@Mat所说的,如果这真的是我想要的。明天将进一步更新

goals_to_conj([G|Gs],Conj) :- 
  goals_to_conj_(Gs,G,Conj).

goals_to_conj_([],G,nonvar(G)).
goals_to_conj_([G|Gs],G0,(nonvar(G0),Conj)) :-
  goals_to_conj_(Gs,G,Conj).

max_seq_automaton_t(Max,Seq,A,T):-
  Max #>=L,
  fd_length(Seq,L),
  maplist(var_t,Seq,Var_T_List), %find var_t for each member of seq
  maplist(=(false),Var_T_List),  %check that all are false i.e no uninstaninated vars
  call(A),!,
  T=true.
max_seq_automaton_t(Max,Seq,A,T):-
  Max #>=L,
  fd_length(Seq,L),
  maplist(var_t,Seq,Var_T_List), %find var_t for each member of seq
  maplist(=(false),Var_T_List),  %check that all are false i.e no uninstaninated vars
  \+call(A),!,
  T=false.
max_seq_automaton_t(Max,Seq,A,T):-
  Max #>=L,
  fd_length(Seq,L),
  maplist(var_t,Seq,Var_T_List), %find var_t for each
  memberd_t(true,Var_T_List,true), %at least one var
  goals_to_conj(Seq,GoalForWhen),
  when(GoalForWhen,(A,T=true)).
max_seq_automaton_t(Max,Seq,A,T):-
  Max #>=L,
  fd_length(Seq,L),
  maplist(var_t,Seq,Var_T_List), %find var_t for each
  memberd_t(true,Var_T_List,true), %at least one var
  goals_to_conj(Seq,GoalForWhen),
  when(GoalForWhen,(\+A,T=false)).

在我看来,您在Prolog方面取得了很大的进步。不过,在这一点上,采取更谨慎的行动是有道理的。原则上,你所要求的一切都可以很容易地解决。您只需要一个
freeze/2
的泛化,它在/2时作为
提供

然而,让我们后退一步,更深入地思考这里到底发生了什么。< /P> 声明性地说,当我们声明一个约束时,我们的意思是它保持不变。我们的意思不是“只有当所有东西都实例化时它才有效”,因为这将把约束简化为一个简单的检查器,从而产生一种“生成并测试”的方法。约束的要点就是尽可能地删减,在许多情况下会大大减少搜索空间

具体化的约束也是如此。当我们发布物化约束时,我们声明物化有效。不仅在所有东西都实例化的情况下,而且总是。关键是(具体化的)约束可以在所有方向上使用。如果被具体化的约束已经包含,我们就会知道它。同样,如果它不能坚持,我们也会了解它。如果有任何一种可能,我们都需要明确地寻找解决方案,或者确定不存在任何解决方案。如果我们想要坚持被物化的约束是成立的,这是很容易的;等等

然而,在所有情况下,重点都是我们可以关注约束的声明性语义,而不需要额外的逻辑和过程性考虑,比如实例化什么以及何时实例化。如果我回答了你字面上的问题,它将使你更接近操作考虑,比你实际需要或想要的要近得多

因此,我不打算回答你的字面问题。但我会给你一个解决你的实际问题的方法

重点是重新定义
自动机/3
。约束具体化本身不会删减任何内容,只要它是开放的,无论被具体化的约束是否实际持有。只有当我们坚持要具体化的约束成立时,传播才会发生

通过具体化构成其分解的约束的连接,很容易具体化
自动机/3
。以下是一种方法,它基于SWI Prolog中免费提供的代码:

:- use_module(library(clpfd)).

automaton(Vs, Ns, As, T) :-
        must_be(list(list), [Vs,Ns,As]),
        include_args1(source, Ns, Sources),
        include_args1(sink, Ns, Sinks),
        phrase((arcs_relation(As, Relation),
                nodes_nums(Sinks, SinkNums0),
                nodes_nums(Sources, SourceNums0)), [[]-0], _),
        phrase(transitions(Vs, Start, End), Tuples),
        list_to_drep(SinkNums0, SinkDrep),
        list_to_drep(SourceNums0, SourceDrep),
        (   Start in SourceDrep #/\
            End in SinkDrep #/\
            tuples_in(Tuples, Relation)) #<==> T.


include_args1(Goal, Ls0, As) :-
        include(Goal, Ls0, Ls),
        maplist(arg(1), Ls, As).

list_to_drep([L|Ls], Drep) :-
        foldl(drep_, Ls, L, Drep).

drep_(L, D0, D0\/L).

transitions([], S, S) --> [].
transitions([Sig|Sigs], S0, S) --> [[S0,Sig,S1]],
        transitions(Sigs, S1, S).

nodes_nums([], []) --> [].
nodes_nums([Node|Nodes], [Num|Nums]) -->
        node_num(Node, Num),
        nodes_nums(Nodes, Nums).

arcs_relation([], []) --> [].
arcs_relation([arc(S0,L,S1)|As], [[From,L,To]|Rs]) -->
        node_num(S0, From),
        node_num(S1, To),
        arcs_relation(As, Rs).

node_num(Node, Num), [Nodes-C] --> [Nodes0-C0],
        { (   member(N-I, Nodes0), N == Node ->
              Num = I, C = C0, Nodes = Nodes0
          ;   Num = C0, C is C0 + 1, Nodes = [Node-C0|Nodes0]
          ) }.

sink(sink(_)).

source(source(_)).
示例:

?- seq([X,1], T).
结果(省略):发布约束,不传播任何内容

下一个例子:

?- seq([X,1], T), X = 3.
X = 3,
T = 0.
?- seq([1,1], T), indomain(T).
T = 0 ;
T = 1.
显然,在这种情况下,具体化的
自动机/3
约束并不成立。然而,具体化约束当然仍然保持不变,这就是为什么在这种情况下
T=0

下一个例子:

?- seq([X,1], T), X = 3.
X = 3,
T = 0.
?- seq([1,1], T), indomain(T).
T = 0 ;
T = 1.
哦哦!这是怎么回事?约束怎么可能是真的和假的?这是因为我们没有看到本例中实际发布的所有约束。使用
call\u residence\u vars/2
查看全部真相

事实上,请在更简单的示例中尝试:

?- call_residue_vars(seq([1,1],0), Vs).
在这种情况下,仍需满足的未决剩余约束为:

_G1496 in 0..1,
_G1502#/\_G1496#<==>_G1511,
tuples_in([[_G1505,1,_G1514]], [[0,0,0],[0,1,1],[1,0,0],[1,1,2],[2,0,2], [2,1,2]])#<==>_G825,
tuples_in([[_G831,1,_G827]], [[0,0,0],[0,1,1],[1,0,0],[1,1,2],[2,0,2],[2,1,2]])#<==>_G826,
_G829 in 0#<==>_G830,
_G830 in 0..1,
_G830#/\_G828#<==>_G831,
_G828 in 0..1,
_G827 in 2#<==>_G828,
_G829 in 0..1,
_G829#/\_G826#<==>0,
_G826 in 0..1,
_G825 in 0..1
现在,我们可以粘贴剩余程序(由CLP(FD)约束组成),并使用
label_fixpoint/1
标记域有限的变量:

?- Vs0 = [_G1496, _G1499, _G1502, _G1505, _G1508, _G1511, _G1514, _G1517, _G1520, _G1523, _G1526],
  _G1496 in 0..1,
  _G1502#/\_G1496#<==>_G1511,
  tuples_in([[_G1505,1,_G1514]], [[0,0,0],[0,1,1],[1,0,0],[1,1,2],[2,0,2], [2,1,2]])#<==>_G825,
  tuples_in([[_G831,1,_G827]], [[0,0,0],[0,1,1],[1,0,0],[1,1,2],[2,0,2],[2,1,2]])#<==>_G826,
  _G829 in 0#<==>_G830, _G830 in 0..1,
  _G830#/\_G828#<==>_G831, _G828 in 0..1,
  _G827 in 2#<==>_G828, _G829 in 0..1,
  _G829#/\_G826#<==>0, _G826 in 0..1, _G825 in 0..1,
  include(finite, Vs0, Vs),
  label(Vs).
练习:无论多么间接,原始查询(即
seq([1,1],0)
)是否无法保持

因此,总结一下:

  • 约束具体化本身不会导致正在具体化的约束的传播
  • 约束具体化通常可以让您检测到约束无法保持
  • 一般来说,CLP(FD)传播必然是不完整的,也就是说,我们不能仅仅因为查询成功就确定有解决方案
  • labeling/2
    允许您查看如果域是有限的,是否存在具体的解决方案
  • 要查看所有挂起的约束,请将查询包装在
    call\u residence\u vars/2
  • 只要未决约束仍然存在,这只是一个有条件的答案

  • <强>推荐< /强>:为了确保不存在挣扎的约束,请在CalueSudiueVAs/2 中查找查询,并查找TopPurror上的任何剩余约束。

    < P>考虑使用广泛可用的谓词<代码> //< 2 /代码>(详情请考虑阅读)。 请注意,原则上,您可以这样实现
    freeze/2

    freeze(V,Goal) :-
       when(nonvar(V),Goal).
    

    在我看来,您正在实施的是以下内容的变体:

    delayed_until_ground_t(Goal,T) :- ( ground(Goal) -> ( call(Goal) -> T = true ; T = false ) ; T = true, when(ground(Goal),once(Goal)) ; T = false, when(ground(Goal), \+(Goal)) ). 延迟到地面(目标,t):- (地面(目标) ->(c)
    freeze(V,Goal) :-
       when(nonvar(V),Goal).
    
    delayed_until_ground_t(Goal,T) :- ( ground(Goal) -> ( call(Goal) -> T = true ; T = false ) ; T = true, when(ground(Goal),once(Goal)) ; T = false, when(ground(Goal), \+(Goal)) ).