3

私の最終的な目標は、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.

すべての変数がインスタンス化されたときに、Goal が 1 回だけ呼び出されるように、これを変数のリストで機能するように拡張するにはどうすればよいですか?

このメソッドでは、複数の変数がある場合、望ましくないこの動作を取得できます。

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

3 に答える 3

4

私の見解では、あなたは Prolog で大きな進歩を遂げています。この時点で、もう少し慎重に進めることは理にかなっています。あなたが求めていることはすべて、原則として簡単に解決できます。freeze/2として利用できるの一般化だけが必要ですwhen/2

しかし、一歩下がって、ここで実際に何が起こっているのかをより深く考えてみましょう。

宣言的に、制約を述べるとき、それが成り立つことを意味します。「すべてがインスタンス化された場合にのみ保持される」という意味ではありません。これは、制約を単なるチェッカーに減らし、「生成してテストする」アプローチにつながるためです。制約のポイントは、可能な限り剪定することであり、多くの場合、検索スペースが大幅に削減されます。

具体化された制約についてもまったく同じことが言えます。具体化された制約を投稿するとき、具体化が保持されていると述べます。すべてがインスタンス化される場合だけでなく、常に. 要点は、(具体化された) 制約がすべての方向で使用できるということです。具体化されている制約がすでに含意されている場合、それを知ることができます。同様に、保持できない場合は、それを知るようになります。いずれかの可能性がある場合は、解決策を明示的に検索するか、解決策が存在しないと判断する必要があります。具体化されている制約が成立することを主張したい場合、それは簡単に可能です。等

ただし、すべてのケースで重要なのは、制約の宣言的なセマンティクスに焦点を当てることができるということです。何がインスタンス化されるのか、いつインスタンス化されるのかなど、論理外の手続き上の考慮事項から解放されます。私があなたの文字通りの質問に答えた場合、実際に必要または望んでいるよりもはるかに運用上の考慮事項に近づくことになります.

したがって、文字通りの質問にはお答えしません。しかし、実際の根本的な問題に対する解決策を提供します。

ポイントは具体化することです automaton/3。制約の具体化は、具体化されている制約が実際に保持されているかどうかに関係なく、それが開いている限り、それ自体では何も刈り込みません。具体化されている制約が保持されると主張する場合にのみ、伝播が発生します。

automaton/3分解を構成する制約の結合を具体化することにより、 を具体化するのは簡単です。SWI-Prolog で自由に利用できるコードに基づいて、これを行う 1 つの方法を次に示します。

:- 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制約が成り立たないことは明らかです。ただし、具体化T=0の制約はもちろん、いつものように依然として保持されます。これが、この場合の理由です。

次の例:

?- seq([1,1], T), indomain(T).
T = 0 ;
T = 1.

おおおお!ここで何が起こっているのですか?制約が true と false の両方であるとはどのように言えますか? これは、この例で実際に投稿されたすべての制約が表示されないためです。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

したがって、上記は、まだヒラメと言われているこれらの制約も保持される場合にのみ保持されます。

以下は、残りの有限ドメイン変数にラベルを付けるのに役立つ補助的な定義です。この例では十分です。

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)保持できないということになりますか?

要約すると、次のようになります。

  1. 制約の具体化自体は、具体化されている制約の伝播を引き起こしません。
  2. 制約の具体化により、多くの場合、制約が保持できないことを検出できます。
  3. 一般に、CLP(FD) の伝播は必然的に不完全です。つまり、クエリが成功したからといって、解決策があるとは確信できません。
  4. labeling/2ドメインが有限である場合、具体的な解があるかどうかを確認できます。
  5. 保留中の制約をすべて表示するには、クエリを でラップしますcall_residue_vars/2
  6. 保留中の制約が残っている限り、それは条件付きの回答にすぎません。

推奨事項: 難しい制約が残っていないことを確認するには、クエリをラップcall_residue_vars/2して、トップレベルで残っている制約を探します。

于 2015-10-14T16:57:10.187 に答える
2

広く利用可能な coroutining述語の使用を検討してwhen/2ください (詳細については、SICStus Prolog のマニュアル ページを参照してくださいwhen/2)。

freeze/2原則として、次のように実装できることに注意してください。

freeze(V,Goal) :-
   when(nonvar(V),Goal).
于 2015-10-14T18:55:44.793 に答える
1

あなたが実装しているのは、次のバリエーションのように見えます。

delayed_until_ground_t(ゴール、T) :-
   ( グラウンド(ゴール)
   -> ( コール(ゴール)
      -> T = 真
      ; T = 偽
      )
   ; T = true, when(ground(ゴール),once(ゴール))
   ; T = false, when(ground(ゴール), \+(ゴール))
   )。

目標遅らせることは非常に優れた機能ですが、永遠に遅らせることの危険性に注意してください。

@mat による上記の回答を必ず読んで消化してくださいcall_residue_vars/2

于 2015-10-16T20:49:02.817 に答える