I'm super new to prolog (swi prolog) so sorry if this is a dumb question. Also this is a uni assignment. I was asked to find the product of a list and this is what I came up with:
product([],1).
product([H|T], Product) :-
product( T, Product1),
Product is H * Product1.
For the longest I had the base case as:
product([],0).
But that makes everything zero. However, when testing the base case of
product([],Product)
, I get one - which is incorrect. Any hints to a solution will be appreciated.
I agree to the viewpoint expressed by @Boris, but I think that if you want 0 as a result for an empty lists, you could exploit pattern matching, one of distinctive features of Prlog:
Since the base case introduced overlaps with the recursive clause, a cut is needed, to avoid
So please change my suggestion as required.
edit to avoid the cut, an alternative pattern could be
now second and third clauses don't overlap anymore, but could be that your Prolog isn't smart enough to optimize the predicate...
As often when describing relations involving lists, it makes sense to start with describing two cases: One for the empty list, and one for lists involving at least one element.
In the second clause, you can use
foldl/4
and a small auxiliary predicate. For example:And in fact you can equivalently write this as:
The product of the empty list is sensibly defined as 1 so that, when building the product over several lists, empty lists blend in naturally and do not change any results.
Examples:
and:
This is not incorrect: 1 is the identity element of "numbers" under multiplication. You can for example abstract away the operation, and the loop, to have something along the lines of:
So you only need to define
identity_element/2
and the operation itself. So, addition and multiplication would be something like:And then you can say:
Similarly, the identity element of string concatenation is the empty string. So, if you just add the following clause to
identity_element/2
:You can now say:
Of course, most of this is unnecessary, but it shows the point.
As for the other answer: there is maybe a cleaner way than a cut to avoid incorrect answers:
So now:
But this feels wrong. You should still have a base case:
This clause of
product/2
simply defines what your identity element is; this is why it is still better to leave the 1 there and not replace it with a 0! However, you will do one multiplication less for non-empty lists (you won't have the last* 1
). This is a valid and easy-to-make optimization. If you try to find the product of a type that does not have an identity element at all: then,product([], X)
is not defined. You can either leave out the clause, then?- product([], X)
will fail. Or, you can maybe throw an error?I would suggest that the product of an empty list should be 1.
First reason, as CapelliC's answer shows, if you require it to be 0, then you have a logical discontinuity. The predicate needs special handling to deal with a 0 product versus a 1.
There's no special reason for the product of an empty list to be 0. The fact:
Describes a relation
foo
between[]
andX
. What shouldX
be? Just because the list is empty doesn't mean that the logical answer is0
.Let's look at another perspective. Suppose I have two lists of numbers,
L1
andL2
. And suppose the products of these lists arep1
andp2
, respectively. Then if I have:What should
X
be? I would think it would bep1 x p2
. IfL1 = [2, 3]
andL2 = [4, 5]
, then we havep1 = 6
,p2 = 20
.L = [2, 3, 4, 5]
, whose product is 120, which is6 x 20
. So far so good.Now suppose
L2 = []
. ThenL = [2, 3]
. Ifproduct(L2, 0)
, then I'd havep2 = 0
, andp1 x p2 = 0
. Butproduct(L, 6).
so the logic breaks down and we have mathematical or logical discontinuity. However, ifproduct([], 1).
, then this all works as expected.In an arithmetic sense, algebraic identity is what you have by default if there is nothing. For sums, the identity is 0, and I can write
a
asa + 0 + ... + 0
and still havea
. Likewise,a
can be writtena x 1 x ... x 1
. So it makes mathematical sense forproduct([], 1).
to be true.