Unknown procedure card/2 in knights and knaves puzzle - prolog

I'm programming some knight and knaves puzzles using both sat/1 and a more natural language approach using the custom propositions A says B and false().
Question 3 is stated as follows:
You meet three inhabitants, A, B and C.
A says: "All three of us are knaves".
B says: "Exactly one of us is a knight".
However, in my solutions that use custom propositions for some reason prolog is giving me Unknown procedure card/2. Here's the code (see question3_again proposition).
question3(A,B,C):- sat(A =:= card([0],[A,B,C])), sat(B =:= card([1],[A,B,C])).
% Now let's make it more intuitive to work with prolog by creating our own operator:
:- op(900,xfy,says).
knight says S :- S.
knave says S :- false(S).
false(A = B) :- dif(A,B).
false( (A ; B) ) :- false(A), false(B).
false( (A , B) ) :- false(A); false(B).
question3_again(A,B,C) :- A says ( A = knave, B = knave, C = knave ),
B says ( card( [1], [A = knight, B = knight, C = knight] ) ).
I tried counting the number of knights and using this solution below instead, but it gives me incorrect answers( I added false(A #= B) :- A #\= B. so false could reason about integers):
false(A #= B) :- A #\= B.
counte(_,[],Count,Count).
counte(E,[H|T],C,Count) :- (E = H, CC is C+1 ; CC is C), counte(E,T,CC,Count).
counte(E,L,Count) :- counte(E,L,0,Count).
question3_again(A,B,C) :- counte(knight,[A,B,C],Knights),
A says ( Knights #= 0 ),
B says ( Knights #= 1 ).
Can someone give me a light?
Thank you in advance!

question3_again(A,B,C) :- A says ( A = knave, B = knave, C = knave ),
B says (permutation([A, B, C], [knave, knave, knight])).
?- question3_again(A, B, C).
A = C, C = knave,
B = knight
With permutation you will get same solution multiple times. If you want to, you can avoid that with some thing like:
one_knight(X) :- nth0(_, X, knight, [knave, knave]).
?- one_knight(X).
X = [knight, knave, knave] ;
X = [knave, knight, knave] ;
X = [knave, knave, knight] ;
false.
permutation would have given 6 choices.
Now your solution will be:
question3_again(A,B,C) :- A says ( A = knave, B = knave, C = knave ),
B says (one_knight([A, B, C])).

Related

; (or) operator in Prolog not returning true unless the left side is true, even if the right side of the operator returns true by itself

I'm trying to return true if either of the two rules on opposite sides of an or operator succeed in Prolog. It only works if what's on the left side of the or operator is found to be true.
It seems like my code should work according to http://www.cse.unsw.edu.au/~billw/dictionaries/prolog/or.html.
Case2 works when case1 is commented out, so it should be returning true, but because it is on the right side of the operator, it isn't. (?)
For clarity, the parameters mean Person1, Person2, TypeOfCousinsTheyAre, DegreesRemovedTheyAre. I am trying to write rules that determine whether two people are first-cousins-once-removed.
Here is the line that uses the or operator which won't return true if the right side is true:
cousins(A, B, 1, 1) :- ( cousinsCase1(A, B, 1, 1) ; cousinsCase2(A, B, 1, 1) ).
Other things I have tried:
(1) Omitting the or operator and writing two identical functions, but whenever they are called and the top one fails, my program crashes.
cousins(A, B, 1, 1) :- var(FirstCousin),
cousin(A, FirstCousin, 1, 0),
parent(FirstCousin, B),
A \= B.
cousins(A, B, 1, 1) :- var(P1),
parent(P1, A),
cousin(P1, B, 1, 0),
A \= B,
A \= P1,
B \= P1.
(2) I have also tried an if-statement to call the other function if the first one fails, but it crashes if the first case fails again.
cousins(A, B, 1, 1) :- cousinsCase1(A, B, 1, 1) -> true ; cousinsCase2(A, B, 1, 1)).
Is there a different way to call the other rule if the first one fails?
EDIT
To take the advice given, here is more of the code:
Facts:
parent(gggm, ggm).
parent(ggm, gm).
parent(gm, m).
parent(m, self).
parent(self, d).
parent(d, gd).
parent(gggm, gga).
parent(gga, c12a).
parent(c12a, c21a).
parent(c21a, c3).
parent(ggm, ga)
parent(ga, c11a).
parent(c11a, c2).
parent(gm, a).
parent(a, c1).
parent(m, s).
parent(s, n).
parent(n, gn).
parent(c1, c11b).
parent(c11b, c12b).
parent(c2, c21b).
parent(c21b, c22).
parent(c3, c31).
parent(c31, c32).
Other rules I have written in order to get the above ones to work:
% Sibling Rule
sibling(A, B) :- parent(P, A), parent(P, B), A \= B.
% First-cousin Rule:
cousin(A, B, 1, 0) :- sibling(P1, P2), parent(P1, A), parent(P2, B).
% Second-cousin Rule:
cousin(A, B, 2, 0) :- parent(P1, A),
parent(P2, B),
parent(PP1, P1), % your grandparent
parent(PP2, P2), % your grand-aunt/uncle
sibling(PP1, PP2). % they're siblings
% 3rd-cousin and more Rule
cousin(A, B, M, 0) :- ReducedM = M - 1,
cousin(A, B, ReducedM, 0).
Calls to the above rules: Sidenote: Both calls do work but the problem is getting them both to work without commenting out the other rule:
cousins(self, c11b, 1, 1).
This call corresponds to the first "1st-cousin, once-removed" case and the case returns the correct answer of true if the other case is commented out.
cousins(self, c11a, 1, 1).
This call corresponds to the second "1st-cousin, once-removed" case and the case returns the correct answer of true if the other case is commented out.
This is a comment in an answer because it will not format correctly in a comment.
What most beginners to Prolog don't realize early enough is that Prolog is based on logic (that they realize) and the three basics operators of logic and, or and not are operators in Prolog, namely (, ; \+). It is not realizing those operators for what they really are.
Starting with not which in Prolog use to be not/1 but is now commonly (\+)/1.
?- \+ false.
true.
?- \+ true.
false.
or using the older not/1 which you can use but is like speaking in a Shakespearean play because it is no longer done this way. I am including this here because many older examples still have it in the examples this way.
?- not(true).
false.
?- not(false).
true.
Next is and which in Prolog is ,/2.
The reason many new Prolog users don't see this as logical and is that a , in many other programming languages is seen as a statement separator (Ref) and acting much like a , in an English sentence. The entire problem with understating , in programming is that it is really an operator and is used for so many things that programmers don't even realize that it should almost always be thought of as an operator but with many different meanings, (operator overloading). Also because , is used as a statement separator, the statements are typically put on separate lines and some programmers even think that a comma (,) is just a statement end like a period (.) is a line end in a sentence; that is not the way to think of these single character operators. They are operators and need to be seen and comprehended as such.
So now that you know where and how your ideas that cause you problems are coming from, the next time you see a comma , or a period . in a programming language really take time to think about what it means.
?- true,true.
true.
?- true,false.
false.
?- false,true.
false.
?- false,false.
false.
Finally logical or which in Prolog is ;/2 or in DCGs will appear as |/2. The use of |/2 in DCGs is the same as | in BNF.
?- true;true.
true ;
true.
?- true;false.
true ;
false.
?- false;true.
true.
?- false;false.
false.
The interesting thing to note about the results of using or (;) in Prolog is that it will they will return when true as many times as one of the propositions is true and false only when all of the propositions are false. (Not sure if proposition is the correct word to use here). e.g.
?- false;false;false.
false.
?- false;false;true.
true.
?- true;false;true.
true ;
true.
?- true;true;true.
true ;
true ;
true.
In case you didn't heed my warning about thinking about the operators when you see them, how many of you looked at
?- true,true.
true.
and did not think that would commonly be written in source code as
true,
true.
with the , looking like a statement end. , is not a statement end, it is the logical and operator. So do yourself a favor and be very critical of even a single , as it has a specific meaning in programming.
A reverse way to get this idea across is to use the addition operator (+) like a statement end operator which it is not but to someone new to math could be mistakenly taken to be that as seen in this reformatting of a simple math expression.
A =
1 +
2 +
3
That is not how one is use to seeing a simple math expression, but in the same way how some programmers are looking at the use of the , operator.
Over the years one thing I have seen that divides programmers who easily get this from the programmers who struggle with this all their careers are those that do well in a parsing class easily get this because they have to parse the syntax down to the tokens such as ,, then convert that into the semantics of the language.
For more details see section 1.2. Control on page 23 of this paper.
EDIT
You really need to use test cases. Here are two to get you started.
This is done using SWI-Prolog
:- begin_tests(family_relationship).
sibling_test_case_generator(ggm ,gga ).
sibling_test_case_generator(gga ,ggm ).
sibling_test_case_generator(gm ,ga ).
sibling_test_case_generator(ga ,gm ).
sibling_test_case_generator(m ,a ).
sibling_test_case_generator(a ,m ).
sibling_test_case_generator(self,s ).
sibling_test_case_generator(s ,self).
test(01,[forall(sibling_test_case_generator(Person,Sibling))]) :-
sibling(Person,Sibling).
cousin_1_0_test_case_generator(gm ,c12a).
cousin_1_0_test_case_generator(ga ,c12a).
cousin_1_0_test_case_generator(m ,c11a).
cousin_1_0_test_case_generator(a ,c11a).
cousin_1_0_test_case_generator(self,c1 ).
cousin_1_0_test_case_generator(s ,c1 ).
cousin_1_0_test_case_generator(d ,n ).
cousin_1_0_test_case_generator(c12a,gm ).
cousin_1_0_test_case_generator(c12a,ga ).
cousin_1_0_test_case_generator(c11a,m ).
cousin_1_0_test_case_generator(c11a,a ).
cousin_1_0_test_case_generator(c1 ,self).
cousin_1_0_test_case_generator(c1 ,s ).
cousin_1_0_test_case_generator(n ,d ).
test(02,[nondet,forall(cousin_1_0_test_case_generator(Person,Cousin))]) :-
cousin(Person, Cousin, 1, 0).
:- end_tests(family_relationship).
EDIT
By !Original:J DiVector: Matt Leidholm (LinkTiger) - Own work based on: Cousin tree.png, Public Domain, Link
This is an answer.
Using this code based on what you gave in the question and a few changes as noted below this code works. Since you did not give test cases I am not sure if the answers are what you expect or need.
parent(gggm, ggm).
parent(ggm, gm).
parent(gm, m).
parent(m, self).
parent(self, d).
parent(d, gd).
parent(gggm, gga).
parent(gga, c12a).
parent(c12a, c21a).
parent(c21a, c3).
parent(ggm, ga).
parent(ga, c11a).
parent(c11a, c2).
parent(gm, a).
parent(a, c1).
parent(m, s).
parent(s, n).
parent(n, gn).
parent(c1, c11b).
parent(c11b, c12b).
parent(c2, c21b).
parent(c21b, c22).
parent(c3, c31).
parent(c31, c32).
% Sibling Rule
sibling(A, B) :-
parent(P, A),
parent(P, B),
A \= B.
% First-cousin Rule:
cousin(A, B, 1, 0) :-
sibling(P1, P2),
parent(P1, A),
parent(P2, B).
% Second-cousin Rule:
cousin(A, B, 2, 0) :-
parent(P1, A),
parent(P2, B),
parent(PP1, P1), % your grandparent
parent(PP2, P2), % your grand-aunt/uncle
sibling(PP1, PP2). % they're siblings
% 3rd-cousin and more Rule
cousin(A, B, M, 0) :-
% ReducedM = M - 1,
ReducedM is M - 1,
ReducedM > 0,
cousin(A, B, ReducedM, 0).
cousinsCase1(A, B, 1, 1) :-
% var(FirstCousin),
cousin(A, FirstCousin, 1, 0),
parent(FirstCousin, B),
A \= B.
cousinsCase2(A, B, 1, 1) :-
% var(P1),
parent(P1, A),
cousin(P1, B, 1, 0),
A \= B,
A \= P1,
B \= P1.
cousins(A, B, 1, 1) :-
(
cousinsCase1(A, B, 1, 1)
;
cousinsCase2(A, B, 1, 1)
).
The first change was as Paulo noted and the checks for var/2 were commented out.
The next change was to change = to is.
The third change to stop infinite looping was to add ReducedM > 0,.
This query now runs.
?- cousins(Person,Cousin,1,1).
Person = gm,
Cousin = c21a ;
Person = ga,
Cousin = c21a ;
Person = m,
Cousin = c2 ;
Person = a,
Cousin = c2 ;
Person = self,
Cousin = c11b ;
Person = s,
Cousin = c11b ;
Person = d,
Cousin = gn ;
Person = c12a,
Cousin = m ;
Person = c12a,
Cousin = a ;
Person = c12a,
Cousin = c11a ;
Person = c11a,
Cousin = self ;
Person = c11a,
Cousin = s ;
Person = c11a,
Cousin = c1 ;
Person = c1,
Cousin = d ;
Person = c1,
Cousin = n ;
Person = n,
Cousin = gd ;
Person = m,
Cousin = c12a ;
Person = self,
Cousin = c11a ;
Person = d,
Cousin = c1 ;
Person = gd,
Cousin = n ;
Person = c21a,
Cousin = gm ;
Person = c21a,
Cousin = ga ;
Person = c11a,
Cousin = c12a ;
Person = c2,
Cousin = m ;
Person = c2,
Cousin = a ;
Person = a,
Cousin = c12a ;
Person = c1,
Cousin = c11a ;
Person = s,
Cousin = c11a ;
Person = n,
Cousin = c1 ;
Person = gn,
Cousin = d ;
Person = c11b,
Cousin = self ;
Person = c11b,
Cousin = s ;
false.

Contradiction in Prolog

I'm testing Prolog ability to test contradiction. To test this, I came up with the following scenario:
There are three suspects: a, b, and c, who are on trial, and one of the suspect is guilty and the rest are innocent.
The facts of the case are,
(Fact 1) if a is innocent then c must be guilty, and
(Fact 2) if a is innocent then c must be innocent.
The answer to who is guilty is suspect 'a' because suspect 'c' cannot be both guilty and innocent. The following code is my implementation:
who_guilty(Suspects) :-
% Knowledge Base
Suspects=[[Name1,Sentence1],
[Name2, Sentence2],
[Name3,Sentence3]],
% Suspects
Names=[a,b,c],
Names=[Name1,Name2,Name3],
% One Is Guilty
Sentences=[innocent,innocent,guilty],
permutation(Sentences,[Sentence1,Sentence2,Sentence3]),
% If A Innocent Then C Is Guilty
(member([a,innocent],Suspects) -> member([c,guilty], Suspects) ; true),
% If A Innocent Then C Is Innocent
(member([a,innocent],Suspects) -> member([c,innocent], Suspects) ; true).
To get Prolog's answer, the query that needs to be run is who_guilty(S). Prolog will then output two identical answers:
S = [[a, guilty], [b, innocent], [c, innocent]] ?
S = [[a, guilty], [b, innocent], [c, innocent]]
My central question is how can I get only one answer instead of two?
Using clpfd library, you can solve this problem easily:
solve(L):-
L = [A,B,C], %0 innocent, 1 guilty
L ins 0..1,
A + B + C #= 1, %one is guilty
A #= 0 #==> C #= 1, %if a is innocent then c must be guilty
A #= 0 #==> C #= 0, %if a is innocent then c must be innocent
label(L).
?- solve(L).
L = [1, 0, 0]
Using clpb :
:- use_module(library(clpb)).
% 0 means guilty
% 1 means innocent
guilty(A,B,C) :-
% only one is guilty
sat(~A * B * C + A * ~B * C + A * B * ~C),
% Fact 1
sat(A =< ~C),
% Fact 2
sat(A =< C).
?- guilty(A,B,C).
A = 0,
B = C, C = 1.
A compact solution, that follows your intuition about expressing the facts.
who_guilty(L) :-
select(guilty,L,[innocent,innocent]),
( L = [innocent,_,_] -> L = [_,_,guilty] ; true ),
( L = [innocent,_,_] -> L = [_,_,innocent] ; true ).
yields:
?- who_guilty(L).
L = [guilty, innocent, innocent] ;
false.
thanks to joel76 (+1), here is a more synthetic solution based on library(clpb)
?- sat(card([1],[A,B,C])*(~A =< ~C)*(~A =< C)).
A = 1,
B = C, C = 0.
1 means guilty...
You should add a new predicate which checks whether someone is both innocent and guilty, which then answers yes to whether there are contradictory outcomes. You are giving Prolog two facts, meaning two correct conclusions to the query. Your real question is "do I have facts contradicting each other?", which means A and NOT A are both true at the same time. contradiction(A, not(A)).
All truths are universal and you are giving two truths that are contradictory to Prolog, so both are true to Prolog.

Prolog, error with exclusive OR

i am new to prolog, and i want to make a simple expert system that uses prepositions, and i am struggling to use exclusive OR. Here is my program so far:
/*facts*/
a.
b.
c.
/*rules*/
e :- c.
d :- a, \+e.
f :- xor(b, d). /*here is where it gives me an error*/
/*query*/
?- f.
/*error*/
ERROR: f/0: Undefined procedure: (xor)/2
Exception: (8) b xor d ? no debug
?-
I don't think this is the better way to solve your problem, but anyway...
?- X is 1 xor 0.
X = 1.
?- X is 1 xor 1.
X = 0.
so
:- meta_predicate xor(0,0).
% reify arguments and apply usual boolean
xor(A, B) :-
( call(A) -> X=1 ; X=0 ),
( call(B) -> Y=1 ; Y=0 ),
1 is X xor Y.
and now
?- xor(true,false).
true.
?- xor(true,true).
false.
?- xor(true,1 is 7-6).
false.

Prolog : get the opposite result

I have the following code :
neighbor(C1, C2, [C1, C2|L]).
neighbor(C1, C2, [C2, C1|L]).
neighbor(C1, C2, [H|L]) :- neighbor(C1, C2, L).
not_neighbors(C5, C2, E, R) :-
not_neighbor(C5, C2, E).
not_neighbors(C5, C2, E, R) :-
not_neighbor(C5, C2, R).
/* THIS DON'T WORK */
not_neighbor(C5, C2, E) :-
\+ neighbor(C5, C2, E).
Or :
same_position(I, J, [I|U], [J|V]).
same_position(I, J, [M|U], [N|V]) :-
I \== M, % optimisation
J \== N, % optimisation
same_position(I, J, U, V).
/* THIS DON'T WORK */
not_under(C4, C1, R, E) :-
\+ same_position(C4, C1, R, E).
I know the problem is the negation and I want to get the opposite result of same_position for example.
M. #CapelliC suggested me to use dif/2 but I don't know how to apply this on my specific example.
Let us first think logically about what it means to be "neighbours" in a list: In what cases are A and B neighbouring elements in a list?
Answer: If the list is of the form [...,X,Y,...] and at least one of the following holds:
A = X and B = Y
A = Y and B = X.
In logical terms: ( A = X ∧ B = Y) ∨ (A = Y ∧ B = X).
We want to state the opposite of this, which is the negated formula:
¬ ( ( A = X ∧ B = Y) ∨ (A = Y ∧ B = X) ) ≡
≡ ¬ ( A = X ∧ B = Y) ∧ ¬ (A = Y ∧ B = X) ≡
≡ (¬ A = X ∨ ¬ B = Y) ∧ (¬ A = Y ∨ ¬ B = X) ≡
≡ (A ≠ X ∨ B ≠ Y) ∧ (A ≠ Y ∨ B ≠ X)
Who would have thought that De Morgan's laws had any practical application, right?
To state X ≠ Y in Prolog, we use the powerful dif/2 constraint, exactly as #CapelliC has already suggested. dif/2 is true iff its arguments are different terms. It is a pure predicate and works correctly in all directions. If your Prolog system does not yet provide it, make sure to let your vendor know that you need it! Until then, you can approximate it with iso_dif/2, if necessary.
In Prolog, the above thus becomes:
( dif(A, X) ; dif(B, Y) ), ( dif(A, Y) ; dif(B, X) )
because (',')/2 denotes conjunction, and (;)/2 denotes disjunction.
So we have:
not_neighbours(_, _, []).
not_neighbours(_, _, [_]).
not_neighbours(A, B, [X,Y|Rest]) :-
( dif(A, X) ; dif(B, Y) ),
( dif(A, Y) ; dif(B, X) ),
not_neighbours(A, B, [Y|Rest]).
A few test cases make us more confident about the predicate's correctness:
?- not_neighbours(a, b, [a,b]).
false.
?- not_neighbours(A, B, [A,B]).
false.
?- not_neighbours(A, B, [_,A,B|_]).
false.
?- not_neighbours(A, B, [_,B,A|_]).
false.
?- not_neighbours(a, b, [_,a,c,_]).
true .
Note that this definition works correctly also if all arguments are variables, which we call the most general case.
A drawback of this solution is that (;)/2 creates many alternatives, and many of them do not matter at all. We can make this significantly more efficient by another algebraic equivalence that lets us get rid of unneeded alternatives:
¬ A ∨ B ≡ A &rightarrow; B
So, in our case, we can write (¬ A = X ∨ ¬ B = Y) as A = X &rightarrow;B≠Y.
We can express implication in Prolog with the powerful if_/3 meta-predicate:
impl(A, B) :- if_(A, B, true).
And so we can declaratively equivalently write our solution as:
not_neighbours(_, _, []).
not_neighbours(_, _, [_]).
not_neighbours(A, B, [X,Y|Rest]) :-
impl(A=X, dif(B, Y)),
impl(B=X, dif(A, Y)),
not_neighbours(A, B, [Y|Rest]).
Sample query:
?- not_neighbours(a, b, [x,y]).
true ;
false.
And a more general case:
?- not_neighbours(a, b, [X,Y]).
X = a,
dif(Y, b) ;
X = b,
dif(Y, a) ;
dif(X, b),
dif(X, a) ;
false.
You can use this predicate for checking and generating answers. Try for example iterative deepening to fairly enumerate all answers:
?- length(Ls, _), not_neighbours(A, B, Ls).
Remarkably, pure logical reasoning has thus led us to a general and efficient Prolog program.
dif/2 may at first appear unusual to you, because it appeared in the very first Prolog system and was then for a time ignored by some vendors. Nowadays, dif/2 is becoming available (again) in an increasing number of implementations as an important built-in predicate that allows you to declaratively state that two terms are different. The massive confusion that its impure alternatives usually cause in Prolog courses can be avoided with dif/2.
If you want to generate the not-neighbors, \+ won't do as it is by definition semidet and never binds a variable. You need something that
constructs answers. One option is to generate all pairs and then use your \+ neighbor(...) to filter the non-neighbors. A direct constructive approach isn't that hard either, although the need to have both orderings complicate the code a little:
not_neighbor(C1, C2, List) :-
append(_, [C10,_|Postfix], List),
member(C20, Postfix),
swap(C1,C2, C10,C20).
swap(X,Y, X,Y).
swap(X,Y, Y,X).
See http://swish.swi-prolog.org/p/njssKnba.pl

Even sum & Odd product of a list, Swi-Prolog [duplicate]

I have a list of numbers, I need to calculate the sum of the even numbers of the list and the product of the odd numbers of the same list. I'm new in Prolog, and my searches so far weren't successful. Can anyone help me solve it ?
l_odd_even([]).
l_odd_even([H|T], Odd, [H|Etail]) :-
H rem 2 =:=0,
split(T, Odd, Etail).
l_odd_even([H|T], [H|Otail], Even) :-
H rem 2 =:=1,
split(T, Otail, Even).
Here is a suggestion for the sum of the even numbers from a list:
even(X) :-
Y is mod(X,2), % using "is" to evaluate to number
Y =:= 0.
odd(X) :- % using even
Y is X + 1,
even(Y).
sum_even(0, []). % empty list has zero sum
sum_even(X, [H|T]) :-
even(H),
sum_even(Y, T),
X is Y+H.
sum_even(X, [H|T]) :-
odd(H),
sum_even(X, T). % ignore the odd numbers
Note: My Prolog has oxidized, so there might be better solutions. :-)
Note: Holy cow! There seems to be no Prolog support for syntax highlighting (see here), so I used Erlang syntax.
Ha, it really works. :-)
Running some queries in GNU Prolog, I get:
| ?- sum_even(X,[]).
X = 0 ?
yes
| ?- sum_even(X,[2]).
X = 2 ?
yes
| ?- sum_even(X,[3]).
X = 0 ?
yes
| ?- sum_even(X,[5,4,3,2,1,0]).
X = 6 ?
yes
The ideas applied here should enable you to come up with the needed product.
Use clpfd!
:- use_module(library(clpfd)).
Building on meta-predicate foldl/4, we only need to define what a single folding step is:
sumprod_(Z,S0,S) :-
M #= Z mod 2,
rem_sumprod_(M,Z,S0,S).
rem_sumprod_(0,Z,S0-P,S-P) :-
S0 + Z #= S.
rem_sumprod_(1,Z,S-P0,S-P) :-
P0 * Z #= P.
Let's fold sumprod_/3 over the list!
l_odd_even(Zs,ProductOfOdds,SumOfEvens) :-
foldl(sumprod_,Zs,0-1,SumOfEvens-ProductOfOdds).
Sample query:
?- l_odd_even([1,2,3,4,5,6,7],Odd,Even).
Odd = 105,
Even = 12.
Alternatively, we can define sumprod_/3 even more concisely by using if_/3 and zeven_t/3:
sumprod_(Z,S0-P0,S-P) :-
if_(zeven_t(Z), (S0+Z #= S, P0=P),
(P0*Z #= P, S0=S)).
untested!
sum_odd_product_even([], S, P, S, P).
sum_odd_product_even([H|T], S0, P0, S, P) :-
S1 is S0 + H,
sum_even_product_odd(T, S1, P0, S, P).
sum_even_product_odd([], S, P, S, P).
sum_even_product_odd([H|T], S0, P0, S, P) :-
P1 is P0 * H,
sum_odd_product_even(T, S0, P1, S, P).
sum_odd_product_even(L, S, P) :-
sum_odd_product_even(L, 0, 1, S, P).
sum_even_product_odd(L, S, P) :-
sum_even_product_odd(L, 0, 1, S, P).
It shouldn't get much simpler than
%
% invoke the worker predicate with the accumulators seeded appropriately.
%
odds_and_evens( [O] , P , S ) :- odds_and_evens( [] , O , 0 , P , S ) .
odds_and_evens( [O,E|Ns] , P , S ) :- odds_and_evens( Ns , O , E , P , S ) .
odds_and_evens( [] , P , S , P , S ) . % if the list is exhausted, we're done.
odds_and_evens( [O] , X , X , P , S ) :- % if it's a single element list, we've only an odd element...
P is X*O , % - compute it's product
. % - and we're done.
odds_and_evens( [O,E|Ns] , X , Y , P , S ) :- % if the list is at least two elements in length'e both an odd and an even:
X1 is X*O , % - increment the odd accumulator
Y1 is Y+E , % - increment the even accumulator
odds_and_evens( Ns , X1 , Y1 , P , S ) % - recurse down (until it coalesces into one of the two special cases)
. % Easy!

Resources