Объединение чистых предикатов
Я пытаюсь объединить некоторые чистые предикаты из предыдущих вопросов о переполнении стека, чтобы создать свой собственный предикат.
Я хочу дать список c (с которыми связаны факты - "at") и термин "особенность", в котором есть оператор и порог для "at". Я хочу разделить списки c, если у c нет соответствующего 'at' из 'feature', он перейдет в ложный раздел, в противном случае оператор проверит '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 может иметь 50 тыс. Ат, как я могу решить требования к памяти? и может ли другой подход использовать нечистые предикаты занимать меньше памяти?
2 ответа
Как вы упомянули, проблема в строке 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]),
Кроме того, это неправильный ответ, так как если у вас есть атрибут (at2,0.03) в Atlist
вы ожидаете memberd_t
возвращать X = 0.03, T = true
который вызовет Then_0
часть if_/3
(и нет другого решения с T = false, которое приведет к другим точкам выбора, выполняющим часть Else_0).
Так что вы можете удалить T = false,dif(X, Y)
из =/3
а теперь давайте попробуем:
?- cpgpartition_ts_fs_feature([c1,c2,c3],Ts,Fs,feature(at2,_,>=,10)).
Fs = [c1, c2].
хорошо, но где Ц?
Итак, есть еще одна ошибка:
Вышесказанное говорит, что это успешно для Fs = [c1,c2]
и за каждый ц. Это потому, что выполнение Else_0
часть if_/3
который выполняет Fs
список, который вы не ограничиваете 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'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 является неудачной, так как она ставит точку выбора на reification, а не на if-then-else itsef. Вот пример недостатка:
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).
Здесь мы видим гораздо лучший интеллект для соединения, например, #/\ от CLP(FD) в SWI-Prolog. Точка выбора не создается:
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 создает точку выбора, что плохо при объединении
condinition. Может быть, мы также можем организовать одинаковые условия в разных местах кода, совместно использовать одну и ту же переменную логического индикатора. Работаю над этим...