组合纯谓词



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

我想给出一个c的列表(它们与它们相关的事实-"ats")和一个"功能"术语,该术语具有运算符和"at"的阈值。我想对 c 的列表进行分区,如果 c 没有来自"功能"的相应"at",它将进入假分区,否则运算符将测试该"c"的"at"并适当地拆分 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].

我认为这是因为以下查询返回的结果带有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,我如何计算内存需求? 使用不纯谓词的不同方法是否可能会占用更少的内存?

正如你提到的,问题出在定义中的 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中有attribute(at2,0.03),您希望memberd_t返回X = 0.03, T = true这将触发if_/3Then_0部分(并且没有其他解决方案,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在哪里??

所以还有另一个错误:

上面说它对Fs = [c1,c2]每个 T都成功。这是因为执行Else_0满足您不限制列表Fs列表if_/3的一部分Ts只是保留为Ts,然后使用另一个独立于 T 的Ts0列表调用cpgpartition_ts_fs_feature(Xs0,Ts0,Fs0,Feature)。所以添加:

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 实现是拙劣的,因为 它把一个选择点放在化上,而不是在 如果-那么-否则它。下面是一个示例缺陷:

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

康迪尼特。也许我们也可以安排同样的条件 在代码的不同位置,共享相同的布尔值 指示符变量。正在努力...

最新更新