我的最终目标是制作automaton / 3的具体版本,如果传递给它的序列中有任何变量,它将冻结。即我不希望自动机实例化变量。
(fd_length / 3,if_ / 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的具体版本,如果传递给它的序列中有任何变量,则会冻结。即我不希望自动机实例化变量。
这就是我所拥有的:
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)).
答案 0 :(得分:4)
在我看来,你在Prolog方面取得了很大的进步。在这一点上,谨慎地继续前进是有道理的。原则上,您要求的所有东西都可以轻松解决。您只需要freeze/2
的一般化,可以when/2
。
但是,让我们退后一步,更深入地考虑这里的实际情况。
声明地说,当我们陈述约束时,我们的意思是它拥有。我们并不是指"它仅在所有内容都被实例化时才成立",因为这会将约束减少到纯粹的检查器,从而导致生成和测试"做法。只要有可能,约束点就是 prune ,在很多情况下会导致搜索空间大大减少。
具体化约束完全相同。当我们发布一个确定的约束时,我们声明具体化成立。不仅在实例化所有内容的情况下,总是。关键在于(已确定的)约束可以在所有方向上使用。如果已经实现了具体化的约束,我们就会知道它。同样,如果它不能成立,我们就会知道它。如果可能出现这种情况,我们需要明确搜索解决方案,或确定不存在解决方案。如果我们想要坚持正在实施的约束,那很容易实现;等
然而,在所有情况下,我们都可以专注于约束的声明性语义,而不受诸如实例化和何时进行的逻辑外过程考虑。如果我回答了你的字面问题,它会让你更接近操作考虑因素,比实际需要或想要的更接近。
因此,我不会回答你的字面问题。但我会给你一个解决你的实际,潜在问题的方法。
重点是 reifiy automaton/3
。约束条件本身不会修剪任何东西,只要它是开放的,无论正在实现的约束是否实际成立。只有当我们坚持正在被强化的约束时才会发生传播。
通过重新组合构成其分解的约束,可以很容易地验证automaton/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(_)).
请注意,只要T
未知,就无法传播 。
我现在使用以下定义进行一些示例查询:
seq(Seq, T) :-
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)], T).
示例:
?- seq([X,1], T).
结果(省略):发布约束,不传播任何内容。
下一个例子:
?- seq([X,1], T), X = 3.
X = 3,
T = 0.
显然,在这种情况下,具体化的automaton/3
约束不。但是, reifying 约束当然一如既往地保留,这就是T=0
在这种情况下的原因。
下一个例子:
?- seq([1,1], T), indomain(T).
T = 0 ;
T = 1.
哦,哦!这里发生了什么?如何约束既真实又错误?这是因为我们没有看到在此示例中实际发布的所有约束。使用call_residue_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
所以,上面只有 if 这些约束,据说仍然是 flounder ,也是如此。
这是一个辅助定义,可帮助您标记剩余的有限域变量。这个例子足够了:
finite(V) :-
fd_dom(V, L..U),
dif(L, inf),
dif(U, sup).
我们现在可以粘贴剩余程序(由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).
请注意,我们无法在原始程序中直接使用标记,即无法执行:
?- call_residue_vars(seq([1,1],0), Vs), <label subset of Vs>.
因为call_residue_vars/2
也会将内部变量带到表面,虽然它们分配了一个域并且看起来像普通的CLP(FD)变量,但并不意味着直接参与任何标记。
相比之下,残差程序可以毫无问题地用于进一步推理,实际上意味着以这种方式使用。
在这个具体的例子中,在上面的情况下标记其域仍然是有限的变量之后,仍然存在一些约束 。它们的形式如下:
tuples_in([[_G1487,1,_G1496]], [[0,0,0],[0,1,1],[1,0,0],[1,1,2],[2,0,2],[2,1,2]])#<==>_G1518
练习:是否会间接地将原始查询(即seq([1,1],0)
,无法容纳?)
所以,总结一下:
labeling/2
可以让您了解是否有具体的解决方案。call_residue_vars/2
。 推荐:为了确保不存在任何挣扎的约束,请将您的查询包装在call_residue_vars/2
中,并在顶层查找任何残留约束。
答案 1 :(得分:2)
考虑使用广泛使用的prolog-coroutining谓词when/2
(有关详细信息,请考虑阅读SICStus Prolog manual page on when/2
)。
请注意,原则上您可以像这样实施freeze/2
:
freeze(V,Goal) :-
when(nonvar(V),Goal).
答案 2 :(得分:1)
您正在实施的内容在我看来是以下变体:
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)) ).
延迟目标可以是一个非常好的功能,但要注意永远拖延的危险。
请务必阅读并理解@mat关于call_residue_vars/2
的上述答案!