结合纯谓词

时间:2017-09-27 11:10:34

标签: prolog logical-purity

我正在尝试将以前的堆栈溢出问题中的一些纯谓词组合起来制作我自己的谓词。

我想给出一个c的列表(其中包含相关的事实 - ''')和一个'特征'一词,它有一个运算符和'at'的阈值。我想对c的列表进行分区,如果c没有来自'feature'的相应'at',它将进入false分区,否则运算符将测试'at'为'c'并拆分c恰当。

例如:

?-cpgpartition_ts_fs_feature([c1,c2,c3],Ts,Fs,feature(at2,_,>=,10)).

应该导致:

Ts = [c3], %c3 has an at2 >= 10
Fs = [c1,c2]. %c1 has at2 <10 and c2 does not have an at2

这是我的代码:

:-use_module(library(clpfd)).

cpgpartition_ts_fs_feature([],[],[],_).
cpgpartition_ts_fs_feature([X|Xs0],Ts,Fs,Feature):-
    Feature = feature(At,_,Op,FValue),
    cpg_ats_i(X,AtList),
    atom_concat(#,Op,Op2), %make clpfd operator
    Test =..[Op2,AtValue3,FValue],
    if_(memberd_t(attribute(At,AtValue3),AtList),
       (
       if_(call(Test), (Ts=[X|Ts0],Fs=Fs0),
       (   Ts =Ts0,Fs=[X|Fs0]))
       )
       ,Fs=[X|Fs0]),
    cpgpartition_ts_fs_feature(Xs0,Ts0,Fs0,Feature).

if_(If_1, Then_0, Else_0) :-
   call(If_1, T),
   (  T == true -> call(Then_0)
   ;  T == false -> call(Else_0)
   ;  nonvar(T) -> throw(error(type_error(boolean,T),_))
   ;  /* var(T) */ throw(error(instantiation_error,_))
   ).

bool01_t(1,true).
bool01_t(0,false).

=(X, Y, T) :-
   (  X == Y -> T = true
   ;  X \= Y -> T = false
   ;  T = true, X = Y
   ;  T = false,
      dif(X, Y)                             % ISO extension
      % throw(error(instantiation_error,_)) % ISO strict
   ).

#=<(X,Y,Truth) :- X #=< Y #<==> B, bool01_t(B,Truth).

#<( X,Y,Truth) :- X #<  Y #<==> B, bool01_t(B,Truth).

#>( X,Y,Truth) :- X #>  Y #<==> B, bool01_t(B,Truth).

#>=(X,Y,Truth) :- X #>= Y #<==> B, bool01_t(B,Truth).

list_memberd_t([]    ,_,false).
list_memberd_t([Y|Ys],X,Truth) :-
   if_(X=Y, Truth=true, list_memberd_t(Ys,X,Truth)).

list_memberd_truth(Xs,X,Truth) :- list_memberd_t(Xs,X,Truth).

memberd_t(X,Xs,Truth) :- list_memberd_t(Xs,X,Truth).

value_intvalue(attribute(_A,X),attribute(_A,Y)):-
        AtValue2 is X *100, %Convert decimal number to integer.
        Y is integer(AtValue2).

cpg_ats_i(C,AtList):-
        cpg_ats(C,Ats),
        maplist(value_intvalue,Ats,AtList).

cpg_ats(c1,[attribute(at1,0.5),attribute(at2,0.03)]).
cpg_ats(c2,[attribute(at1,0.02)]).
cpg_ats(c3,[attribute(at2,0.1),attribute(at3,0.04),attribute(at4,0.08)]).

尝试测试时,我得到:

cpgpartition_ts_fs_feature([c1,c2,c3],Ts,Fs,feature(at2,_,>=,10)).
Fs = [c1, c2] ;
Fs = [c1, c2, c3] ;
Fs = [c1, c2] ;
Fs = [c1, c2, c3].

有趣的是,如果clist的顺序不同,结果会发生变化。

?- cpgpartition_ts_fs_feature([c3,c1,c2],Ts,Fs,feature(at2,_,>=,10)).
Ts = [c3|_12950],
Fs = [c1, c2] ;
Ts = [c3|_12950],
Fs = [c1, c2] ;
Fs = [c3, c1, c2] ;
Fs = [c3, c1, c2].

我认为这是因为以下查询返回带有dif/2约束的结果,这似乎不适合我想做的事情,我只想要具体的解决方案。

    ?- cpg_ats_i(C,Ats),   if_(memberd_t(attribute(at2,AtValue),Ats),Q=true,Q=false).
C = c1,
Ats = [attribute(at1, 50), attribute(at2, 3)],
AtValue = 3,
Q = true ;
C = c1,
Ats = [attribute(at1, 50), attribute(at2, 3)],
Q = false,
dif(AtValue, 3) ;
C = c2,
Ats = [attribute(at1, 2)],
Q = false ;
C = c3,
Ats = [attribute(at2, 10), attribute(at3, 4), attribute(at4, 8)],
AtValue = 10,
Q = true ;
C = c3,
Ats = [attribute(at2, 10), attribute(at3, 4), attribute(at4, 8)],
Q = false,
dif(AtValue, 10).

此外,目标是此代码在大量数据上运行,c的列表长度将达到数十万,每个c可能有50k的ats,我如何计算出内存要求?并且使用不纯的谓词可能占用更少的内存是一种不同的方法吗?

3 个答案:

答案 0 :(得分:3)

正如您所提到的,问题出在定义中的dif(X,Y)行:

=(X, Y, T) :-
   (  X == Y -> T = true
   ;  X \= Y -> T = false
   ;  T = true, X = Y
   ;  T = false,
      dif(X, Y)                             % ISO extension
      % throw(error(instantiation_error,_)) % ISO strict
   ).

那是因为如果你尝试:

memberd_t(attribute(at2,X),[attribute(at1,0.5),attribute(at2,0.03)],T).
X = 0.03,
T = true ;
T = false,
dif(X, 0.03).

此处提供解决方案的选择点:T = false,dif(X, 0.03).将导致执行以下部分Fs=[X|Fs0]

if_(memberd_t(attribute(At,AtValue3),AtList),
       (
       if_(call(Test), (Ts=[X|Ts0],Fs=Fs0),
       (   Ts =Ts0,Fs=[X|Fs0]))
       )
       ,Fs=[X|Fs0]),

这也不是正确的答案,因为如果您希望Atlist memberd_t中的属性(at2,0.03)返回X = 0.03, T = true,则会触发Then_0部分是if_/3(没有其他解决方案,T = false会导致其他选择点执行Else_0部分)。

因此,您可以移除T = false,dif(X, Y)的{​​{1}},现在让我们试试:

=/3

好但是Ts ??

所以还有另一个错误:

上面说的是,?- cpgpartition_ts_fs_feature([c1,c2,c3],Ts,Fs,feature(at2,_,>=,10)). Fs = [c1, c2]. 成功,而每个Ts 成功。这是因为执行Fs = [c1,c2] Else_0部分符合if_/3列表的Fs部分您不会限制Ts列表只留下Ts和稍后使用与Ts无关的另一个cpgpartition_ts_fs_feature(Xs0,Ts0,Fs0,Feature)列表调用Ts0。所以添加:

if_(memberd_t(attribute(At,AtValue3),AtList),
       (
        if_(call(Test), (Ts=[X|Ts0],Fs=Fs0), (Ts =Ts0,Fs=[X|Fs0]))
       )
       ,(Fs=[X|Fs0], Ts = Ts0 )),
                     ^^^^^^^^
                     here added 

最后我按照@false的建议,最好用Test =..[Op2,AtValue3,FValue], ..., call(Test)替换call(Op2,AtValue3,FValue),因为call/N是ISO的一部分,它适合原始的Mycroft O&#39; Keefe类型系统。

现在让我们再试一次:

?- cpgpartition_ts_fs_feature([c1,c2,c3],Ts,Fs,feature(at2,_,>=,10)).
Ts = [c3],
Fs = [c1, c2].

似乎正确且确定性:) !!

至于你问题的记忆部分我不太确定,但更喜欢确定性的谓词,它们不会留下内存效率的选择点。使用纯谓词将使你的程序更具关系性并且会有更好的行为但是我不确定if_/3是否具有如此高效的内存,因为它包含许多调用但我不确定其他人是否可以更清楚地回答这一部分。

答案 1 :(得分:1)

感谢Coder的回答,我提出了:

cpgpartition_ts_fs_feature([],[],[],_).
cpgpartition_ts_fs_feature([X|Xs0],Ts,Fs,feature(At,_,Op,FValue)):-
    cpg_ats_i(X,AtList),
    atom_concat(#,Op,Op2), %make clpfd operator
    maplist(atterm_atname,AtList,Ats),
    if_(memberd_t(At,Ats),
      (
      memberchk(attribute(At,AtValue3),AtList),
      if_(call(Op2,AtValue3,FValue), (Ts=[X|Ts0],Fs=Fs0),
        (   Ts =Ts0,Fs=[X|Fs0]))
      ),
      (Fs=[X|Fs0],Ts=Ts0)
    ),
    cpgpartition_ts_fs_feature(Xs0,Ts0,Fs0,feature(At,_,Op,FValue)).


atterm_atname(attribute(At,_),At).

这使我可以在不更改=/3的定义的情况下获得相同的结果。

答案 2 :(得分:0)

当前建议的if_ / 3的实现被破坏,因为 它将选择点放在确定性上,而不是 如果-然后-其他。这是一个示例缺陷:

Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.4)

?- call(','(X=Y,2=3),B).
X = Y,
B = false ;  %%% a bloody choice point %%%
B = false,
dif(X, Y). 

在这里,我们发现连词的智能性更高 例如来自SWI-Prolog中CLP(FD)的#/ \。没有选择 点已创建:

Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.4)

?- X #= Y #/\ 2 #= 3 #<==> B.
B = 0,
X in inf..sup,
Y in inf..sup. 

我目前正在研究更好的if_ / 3, 这种情报开始发挥作用。基本模式 更好的if_ / 3将是:

if(Cond, Then, Else) :-
   reify(Cond, Bool),
   thenelse(Bool, Then, Else)

thenelse(1, Then, _) :- Then.
thenelse(0, _, Else) :- Else. 

想法是将任何选择点放入reify / 2, 尽可能避免它们。当前(=)/ 3创建 选择点,组合时不好

条件。也许我们也可以安排相同的条件 在代码的不同位置共享相同的布尔值 指标变量。正在努力...