Related
I have two, slightly different, implementations of a predicate, unique_element/2, in Prolog. The predicate succeeds when given an element X and a list L, the element X appears only once in the list. Below are the implementations and the results:
Implementation 1:
%%% unique_element/2
unique_element(Elem, [Elem|T]) :-
not(member(Elem, T)).
unique_element(Elem, [H|T]) :-
member(Elem, T),
H\==Elem,
unique_element(Elem, T),
!.
Results:
?- unique_element(X, [a, a, b, c, c, b]).
false.
?- unique_element(X, [a, b, c, c, b, d]).
X = a ;
X = d.
Implementation 2:
%%% unique_element/2
unique_element(Elem, [Elem|T]) :-
not(member(Elem, T)).
unique_element(Elem, [H|T]) :-
H\==Elem,
member(Elem, T),
unique_element(Elem, T),
!.
In case you didn't notice at first sight: H\==Elem and member(Elem, T) are flipped on the 2nd impl, rule 2.
Results:
?- unique_element(X, [a, a, b, c, c, b]).
X = a.
?- unique_element(X, [a, b, c, c, b, d]).
X = a ;
X = d.
Question: How does the order, in this case, affect the result? I realize that the order of the rules/facts/etc matters. The two specific rules that are flipped though, don't seem to be "connected" or affect each other somehow (e.g. a cut in the wrong place/order).
Note: We are talking about SWI-Prolog here.
Note 2: I am aware of, probably different and better implementations. My question here is about the order of sub-goals being changed.
H\==Elem is testing for syntactic inequality at the point in time when the goal is executed. But later unification might make variables identical:
?- H\==Elem, H = Elem.
H = Elem.
?- H\==Elem, H = Elem, H\==Elem.
false.
So here we test if they are (syntactically) different, and then they are unified nevertheless and thus are no longer different. It is thus just a temporary test.
The goal member(Elem, T) on the other hand is true if that Elem is actually an element of T. Consider:
?- member(Elem, [X]).
Elem = X.
Which can be read as
(When) does it hold that Elem is an element of the list [X]?
and the answer is
It holds under certain circumstances, namely when Elem = X.
If you now mix those different kinds of goals in your programs you get odd results that can only explained by inspecting your program in detail.
As a beginner, it is best to stick to the pure parts of Prolog only. In your case:
use dif/2 in place of \==
do not use cuts - in your case it limits the number of answers to two. As in
unique_element(X, [a,b,c])
do not use not/1 nor (\+)/1. It produces even more incorrectness. Consider unique_element(a,[a,X]),X=b. which incorrectly fails while X=b,unique_element(a,[a,X]) correctly succeeds.
Here is a directly purified version of your program. There is still room for improvement!
non_member(_X, []).
non_member(X, [E|Es]) :-
dif(X, E),
non_member(X, Es).
unique_element(Elem, [Elem|T]) :-
non_member(Elem, T).
unique_element(Elem, [H|T]) :-
dif(H,Elem),
% member(Elem, T), % makes unique_element(a,[b,a,a|Xs]) loop
unique_element(Elem, T).
?- unique_element(a,[a,X]).
dif(X, a)
; false. % superfluous
?- unique_element(X,[E1,E2,E3]).
X = E1, dif(E1, E3), dif(E1, E2)
; X = E2, dif(E2, E3), dif(E1, E2)
; X = E3, dif(E2, E3), dif(E1, E3)
; false.
Note how the last query reads?
When is X a unique element of (any) list [E1,E2,E3]?
The answer is threefold. Considering one element after the other:
X is E1 but only if it is different to E2 and E3
etc.
TL;DR: Read the documentation and figure out why:
?- X = a, X \== a.
false.
?- X \== a, X = a.
X = a.
I wonder why you stop so close from figuring it out yourself ;-)
There are too many ways to compare things in Prolog. At the very least, you have unification, which sometimes can compare, and sometimes does more; than you have equvalence, and its negation, the one you are using. So what does it do:
?- a \== b. % two different ground terms
true.
?- a \== a. % the same ground term
false.
Now it gets interesting:
?- X \== a. % a free variable and a ground term
true.
?- X \== X. % the same free variable
false.
?- X \== Y. % two different free variables
true.
I would suggest that you do the following: figure out how member/2 does its thing (does it use unification? equivalence? something else?) then replace whatever member/2 is using in all the examples above and see if the results are any different.
And since you are trying to make sure that things are different, try out what dif/2 does. As in:
?- dif(a, b).
or
?- dif(X, X).
or
?- dif(X, a).
and so on.
See also this question and answers: I think the answers are relevant to your question.
Hope that helps.
Here is another possibility do define unique_element/2 using if_/3 and maplist/2:
:- use_module(library(apply)).
unique_element(Y,[X|Xs]) :-
if_(Y=X,maplist(dif(Y),Xs),unique_element(Y,Xs)).
In contrast to #user27815's very elegant solution (+s(0)) this version does not build on clpfd (used by tcount/3). The example queries given by the OP work as expected:
?- unique_element(a,[a, a, b, c, c, b]).
no
?- unique_element(X,[a, b, c, c, b, d]).
X = a ? ;
X = d ? ;
no
The example provided by #false now succeeds without leaving a superfluous choicepoint:
?- unique_element(a,[a,X]).
dif(a,X)
The other more general query yields the same results:
?- unique_element(X,[E1,E2,E3]).
E1 = X,
dif(X,E3),
dif(X,E2) ? ;
E2 = X,
dif(X,E3),
dif(X,E1) ? ;
E3 = X,
dif(X,E2),
dif(X,E1) ? ;
no
Can you not define unique_element like tcount Prolog - count repetitions in list
unique_element(X, List):- tcount(=(X),List,1).
I have some problem whith this code. The func3 was never invoked :
technology(board, saw, table).
technology(wood, sanded, board).
technology(water, grow, tree).
material(table, board, 20).
material(table, tree, 5).
material(wood, water, 100).
equipment(table,saw, cut, 10).
equipment(board, plane, polish, 7).
equipment(tree, watering, growing, 100).
specialization(saw, wood).
specialization(plane, wood).
specialization(watering, forestry).
plan_vypusku(table,10).
potreba_u_zahotovkah1(M, V):-
write(M + V),
nl,
technology(F, _, M),
material(M, F, C),
Z is V * C,
write(F - Z),
nl.
func3([A, B], C):-
write("InF3"),
nl,
potreba_u_zahotovkah1(A, C),
func3(B, C).
func2([A, B], C):-
write("InF2"),
nl,
findall(M, equipment(M, A, _, _), ML),
write(ML),
nl,
func3(ML, C),
func2(B, C).
potreba_u_zahotovkah(C, G):-
findall(X, specialization(X, C), XL),
write(XL),
nl,
plan_vypusku(G, S),
func2(XL, S).
Result:
?- potreba_u_zahotovkah(wood,table).
[saw,plane]
InF2
[table]
false.
Help PLS!
I don't know what you're up to, but I have an explanation of the unexpected failure you observed.
The query you made wrote the following lines by side-effect (write/1 and nl/0) and then failed:
?- potreba_u_zahotovkah(wood,table).
[saw,plane]
InF2
[table]
false.
The highlighted line was output by the following highlighted write/1 and nl/0:
func2([A, B], C):-
write("InF2"),
nl,
findall(M, equipment(M, A, _, _), ML),
write(ML),
nl,
func3(ML, C),
func2(B, C).
So above variable ML was bound to [table] when the goal func3(ML, C) was called.
Looking at your definition of func3/2 the reason for failure becomes apparent:
func3([A, B], C):-
write("InF3"),
nl,
potreba_u_zahotovkah1(A, C),
func3(B, C).
The clause head of func3/2 demands that the first argument is a list having exactly two elements. The list [table], however, has exactly one element, not two!
As no more choicepoint are open, the goal potreba_u_zahotovkah(wood,table) fails.
I need help reversing a list.
fun(a, [b, d]).
fun(b, [c]).
fun(c, []).
fun(d, [e]).
fun(e, [f]).
fun(f, [g]).
fun(g, []).
xyz(X, Y):-
fun(X, Z) -> findall([A|B], (member(A, Z), xyz(A, B)), L),
flatten(L, F), sort(F, Y); Y = [].
The query xyz(a,X). gives me X = [b,c,d,e,f,g].
However, I would like it to give me X = [g,f,e,d,c,b].
I have tried different attempts at reversing the list, but I am not having any luck.
I have tried adding an additional predicate right after this, but it didn't work either:
xyz2(X,Y):-
xyz(X,Y),
reverse(Y,Z),
Z\=0.
Credit goes to CapelliC for the approach to the implementation above found at my other post here.
Recursion in PROLOG?
You can avoid some difficult programming, and make your program easier to read by re-defining your problem. Say the f/2 describes a directed graph, with edges from the first argument to each of the elements in the second argument, so:
a ---> b
a ---> d
b ---> c
% etc
Then, your question is, which nodes in the graph are reachable from a given node? You can define the solution with the help of library(ugraphs).
To make all edges from an f/2:
edge(From-To) :-
f(From, L),
member(To, L).
You can now collect the edges, make a graph, and find which nodes are reachable from a starting node:
foo(X, L) :-
findall(E, edge(E), Edges),
vertices_edges_to_ugraph([], Edges, G),
reachable(X, G, All),
once(select(X, All, R)), % remove the node you start from
reverse(R, L).
Per definition, a node is always reachable from itself, so you need to pick it out of the list of reachable nodes.
?- foo(a, X).
X = [g, f, e, d, c, b].
?- foo(e, X).
X = [g, f].
?- foo(g, X).
X = [].
I don't exactly understand why the order of the elements is significant. This feels a bit like a code smell.
The solution
ppath(X,Y,M,Path,[Y|Path]) :- edge(X,Y,M),\+ memberchk(Y,Path).
path(X,Y,P,SoFar,Path) :- edge(X,W,M), \+ memberchk(W,SoFar),
path(W,Y,N,[W|SoFar],Path), P is M+N.
pravilo(X,Y,Z) :-
aggregate(min(W), P^path(X,Y,W,[],P),Z).
Here is the code i have. The question is that starting point is a, and ending point is z.
There is an error after execution, the result is displayed like [z, c, h, b]. But the correct answer should [a,b,c,z].
Please help to fix my problem.
library(aggregate) allows for a witness on min/max scalar operations. We can use that feature to report back the path as well as the travel length:
path(X,Y,M,Path,FullPath) :-
edge(X,Y,M), \+ memberchk(Y,Path),
reverse([Y|Path], FullPath).
path(X,Y,P,SoFar,Path) :-
edge(X,W,M), \+ memberchk(W,SoFar),
path(W,Y,N,[W|SoFar],Path), P is M+N.
pravilo(X,Y,Z,Path) :-
aggregate(min(W,P), P^path(X,Y,W,[X],P), min(Z,Path)).
Note there is a typo in edge/3: edge(b,e,16 should be edge(b,e,16)..
Once corrected the DB, I get
pravilo(a,z,M,P).
M = 16,
P = [a, b, h, c, z].
I'd like to simulate the equivalence in Prolog with the properties of being commutative and transitive, here is what I did: equal/2 will be supplying as facts.
symmetricEqual(A,B):- equal(A,B).
symmetricEqual(A,B):- equal(B,A).
transitiveEqualPath(A,B,_) :- symmetricEqual(A,B).
transitiveEqualPath(B,C,IntermediateNodes) :-
symmetricEqual(A,B),
\+ member(C,IntermediateNodes),
transitiveEqualPath(A,C,[B|IntermediateNodes]), B\==C.
transitiveEqual(A,B) :- transitiveEqualPath(A,B,[]).
But I am running into performance issues with the above solution to try to compute transitiveEqual/2 (it has taken roughly 20mins), I have around 2K symmetricalEqual/2 facts computed pretty fast from equal/2, so it must be the cause of rules for transitiveEqual/2, anybody can suggest any improvement on this?
Thanks very much.
Courtesy of the approach from here:
symmetricEquals(X,Y) :- equal(X,Y).
symmetricEquals(X,Y) :- equal(Y,X).
transitiveEqual(A, B) :-
% look for an equality path from A to B
path(A, B, _Path).
path(A, B, Path) :-
% build a path from A to B
path(A, B, [A], Path).
path(A, B, _Acc, [B]) :-
symmetricEquals(A, B).
path(A, B, Visited, [C|Path]) :-
symmetricEquals(A, C),
C \== B,
\+ memberchk(C, Visited),
path(C, B, [C|Visited], Path).
Note that path/3,4 will backtrack to enumerate all possible paths between any ground or variable A to B. This could be quite expensive if the graph implied by your equal/2 facts is large, contains many disconnected components, and/or you're looking for all combinations.