组合纯谓词

Combining pure predicates

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

我想给出一个 c 的列表(其中有相关的事实 -'ats' )和一个 'feature' 术语,它有一个运算符和一个 'at' 的阈值。我想对 c 的列表进行分区,如果 c 没有来自 'feature' 的相应 'at' 它将进入错误分区,否则操作员将为此测试 '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].

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

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

我认为这是因为以下查询 returns 结果带有 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的list有几十万的长度,每个c可能有50k的ats,我怎么算出来内存要求?使用不纯谓词的不同方法是否可能占用更少的内存?

如您所述,问题出在定义的 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 中具有属性(at2,0.03),您期望 memberd_t 到 return X = 0.03, T = true 这将触发Then_0 if_/3 的一部分(并且没有其他 T = false 的解决方案会导致其他选择点执行 Else_0 部分)。

所以您可以删除 =/3T = false,dif(X, Y) 现在让我们试试:

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

很好,但是 Ts 在哪里?

所以还有一个bug:

上面说对于每个 Ts,Fs = [c1,c2] 都成功。那是因为执行 if_/3Else_0 部分满足 Fs 列表你不限制 Ts 列表只是离开 Ts 然后调用 cpgpartition_ts_fs_feature(Xs0,Ts0,Fs0,Feature) 与另一个独立于 Ts 的 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 的建议,最好用call(Op2,AtValue3,FValue) 替换Test =..[Op2,AtValue3,FValue], ..., call(Test),因为call/N 是ISO 的一部分,它适合原始的Mycroft O'Keefe 类型系统。

现在让我们再试一次:

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

似乎是正确的和确定性的:) !!。

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

感谢 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 的定义的情况下得到相同的结果。

目前建议的 if_/3 实现是拙劣的,因为 它在具体化上放置了一个选择点,而不是在 if-then-else 本身。这是一个示例缺陷:

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 创建 一个选择点,组合起来不好

条件。也许我们也可以安排同样的条件 在代码的不同地方,共享相同的布尔值 指示变量。正在努力...