Tyler
Tyler

Reputation: 73

Check if two list are both even or odd in Prolog

I am debating on whether I should check to see if the two list are even by dividing with / or % to check to see if the two list are both list are even and return true OR both list are odd and return true otherwise return false; with the following code:

evenOrOdd([],[],0).
evenOrOdd(List1, List2, Output) :-
   length(List1, N),
   length(List2, M),
(N/2 && M/2 || N%2 && M%%2 ->
    Output = List1
;   Output = List2
).

The following won't compile right and I can't figure out what the problem would be. Ultimately the two list I'm trying to compare is:

[x,x,x]
[1,2,3]

But when I went to test this it returned false. How come this happen?

Upvotes: 1

Views: 683

Answers (1)

Daniel Lyons
Daniel Lyons

Reputation: 22803

There are several problems with your code, but let me first show you the solution.

evenOrOdd(L, R) :-
  length(L, N),     length(R, M),
  N mod 2 =:= M mod 2.

The first thing you'll notice here is that I had to create a lot of temporary values. Expression evaluation in Prolog is actually triggered by is/2; it doesn't automatically happen. As tempting as it is to write N mod 2 = M mod 2 is, it will only unify if N = M, which is not what you intend. You must trigger the evaluation with is/2 and then compare values directly, or use =:=/2 which performs evaluation itself (thanks @CapelliC!).

(Aside: What you're actually buying with this suspended-evaluation tradeoff is a great deal of DSL-building power, but most beginners in university classes do not ever get to see that, so they just find it a pointless frustration.)

Now to discuss the problems.

First, your code has some "type" confusion: if you are given empty lists, the final variable unifies with 0; if you are given non-empty lists, it will unify with one or the other list. I'm hard pressed to imagine a scenario when that would be meaningful; I halfway wonder if this is a carryover from Python, where 0 has the boolean flavor of false but a non-empty list has the boolean flavor of true. Alternately, it could be that you assumed you needed a base case, even though you don't really use the inductive case of the list. And maybe the third argument is just there to represent a result, because, things have to have results. :) Whatever the case may be, it isn't quite necessary or right.

Second, you are making the honest beginner mistake of assuming that arithmetic expressions are evaluated by Prolog. It's an honest mistake; every other language on earth works this way, but Prolog is different. N/2 && M/2 || N%2 && M %%2 is pretty far from syntactically acceptable Prolog. For one thing, % is the comment character in Prolog; we use mod for modular arithmetic (like Python). Also, because is/2 is about evaluating arithmetic expressions, there is no syntax for boolean "and" and boolean "or," just bitwise, which are not exactly what you need. The regular logical "and" and "or" are , and ; in Prolog, respectively. But I think this expression is more complex than you need anyway; you just need to know that they have the same remainder mod 2.

It's almost always better to allow Prolog's unification machinery to convey true or false on your behalf rather than explicitly unifying true and false with some variable; otherwise you can easily get in strange situations where the predicate has "succeeded" in some sense meant to convey failure. And besides, you're using Prolog! You should be enjoying the benefits of backtracking rather than writing procedural code.

Hope this clears things up!

Upvotes: 3

Related Questions