With the help of library(reif)
also more complex conditions can be reified. In particular, conjunctions and disjunctions of any reified condition. So facts can be encoded as a disjunction (one alternative per fact) of a conjunction (one =
per argument). Of course, neither direct cuts nor a soft cut like *->
can solve this problem alone. See char_class/2
for such an example.
To better illustrate the use of library(reif)
, here is a correct implementation of @brebs' member_other/3
. It is just as determinate as brebs expects it to be, in addition it is correct for the cases where brebs' implementation is was broken. Some benchmarks at the end show that this solution is sometimes the fastest and most space efficient.
member_other(E, Lst, Elem) :-
if_(memberd_t(E, Lst), E = Elem, other(E) = Elem).
?- member_other(E, [a,b], O).
E = a, O = a
; E = b, O = b
; O = other(E), dif(a,E), dif(b,E).
?- member_other(b, [a,b], O).
O = b.
?- member_other(c, [a,b], O).
O = other(c).
?- member_other(a, [a], other(a)).
false.
?- freeze(E,(E=a;E=b)), member_other(E,[c],R).
R = other(E),
freeze(E, (E=a;E=b)). % brebs has a redundant dif(E, c).
?- freeze(E,E < 0), numlist(1,400000,L), time(member_other(E,L,R)).
% 3,600,003 inferences, 0.280 CPU in 0.280 seconds (100% CPU, 12836803 Lips)
% vs. brebs' and newbrebs:
% 13,200,005 inferences, 1.382 CPU in 1.382 seconds (100% CPU, 9554032 Lips)
% 3,600,004 inferences, 0.263 CPU in 0.263 seconds (100% CPU, 13688070 Lips)
?- numlist(1,400000,L), time(member_other_if(-1,L,R)).
% 800,003 inferences, 0.042 CPU in 0.042 seconds (100% CPU, 18944780 Lips)
% vs. brebs' and newbrebs:
% 2,000,004 inferences, 0.203 CPU in 0.203 seconds (100% CPU, 9834699 Lips)
% 800,004 inferences, 0.043 CPU in 0.043 seconds (100% CPU, 18709716 Lips)
?- numlist(1, 4000, L), time(member_other_if(E, L, other(M))).
% 288,115,984 inferences, 14.393 CPU in 14.396 seconds (100% CPU, 20018259 Lips)
% vs. brebs' and newbrebs which is much faster in this case:
% 104,005 inferences, 0.019 CPU in 0.019 seconds (100% CPU, 5563599 Lips)
% 112,005 inferences, 0.037 CPU in 0.037 seconds (100% CPU, 3011414 Lips)
The major difference between both versions can best be seen with the following query:
?- member_other(E,[A,B,C],R).
E = A, R = E
; E = B, R = E, dif:dif(A,E)
; E = C, R = E, dif:dif(A,E), dif:dif(B,E)
; R = other(E), dif:dif(A,E), dif:dif(B,E), dif:dif(C,E).
?- member_other_brebs(E,[A,B,C],R).
E = A, R = E
; E = B, R = E, partially_redundant
; E = C, R = E, partially_redundant
; R = other(E), dif:dif(E,A), dif:dif(E,C), dif:dif(E,B).
So brebs' version admits the same redundancies that also member/2
does. The deeper problem behind is that it seems difficult to ensure that brebs' version is indeed pure and monotonic, as was shown by this lengthy development process.
And here is its expansion for SICStus with module prefixes removed such that it can be used also with systems whose differing goal expansion mechanisms do not produce the optimal result like (currently) SWI and Scryer.
member_other(A,B,C) :-
memberd_t(A,B,D),
( D==true ->
A=C
; D==false ->
other(A)=C
; nonvar(D) ->
throw(error(type_error(boolean,D),type_error(call(user:memberd_t(A,B),D),2,boolean,D)))
; throw(error(instantiation_error,instantiation_error(call(user:memberd_t(A,B),D),2)))
).
memberd_t(A, B, C) :-
i_memberd_t(B, A, C).
i_memberd_t([], _, false).
i_memberd_t([A|B], C, D) :-
( A\=C ->
i_memberd_t(B, C, D)
; A==C ->
D=true
; A=C,
D=true
; dif(A, C),
i_memberd_t(B, C, D)
).