3

I'm trying to multiply two numbers in Prolog recursively i.e. 3*4 = 3+3+3+3 = 12.

My code is :

mult(0,Y,Y).
mult(X,Y,Z) :- 
    NewX is X-1, 
    Z is Y + mult(NewX,Y,Z).

but I keep either in an infinite loop or being told mult is not a function.

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
Ellis Thompson
  • 408
  • 6
  • 18

1 Answers1

2

What you here constructed is a predicate. A predicate is not the same as a function in computer science, you can not write A is B + some_pred(C), or at least not as far as I know in ISO Prolog, and definitely not without adding some extra logic.

In order to pass values, one uses variables. We can thus call the mult/3 predicate recursively, and use a variable that will be unified with the result. We can then perform arithmetic with it, like:

mult(0, _, 0).
mult(X, Y, Z) :-
    X1 is X - 1,
    mult(X1, Y, Z1),
    Z is Y + Z1.

Note that you can not reassign a (different) value to a variable. So if, like you did in the question, use Z twice, then given Y is not 0, this will fail.

The above is however still not sufficient, since it will produce a result, but then get stuck in an infinite loop since if it calls (eventually) mult(0, 4, Z) (4 is here just a value), there are two ways to resolve this: with the base case, and with the recursive case.

We thus need a "guard" for the second case, like:

mult(0, _, 0).
mult(X, Y, Z) :-
    X > 0,
    X1 is X - 1,
    mult(X1, Y, Z1),
    Z is Y + Z1.

We then obtain for example:

?- mult(14, 25, Z).
Z = 350 ;
false.

One can improve the speed of this mult/3 predicate by implementing a version with an accumulator. I leave this as an exercise.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • So it can be improved with an accumulator, what exactly is one? – Ellis Thompson Dec 12 '18 at 22:40
  • "What you here define a predicate." -- not sure that makes sense as a sentence. Maybe you meant "what you have is a predicate"? – Dunes Dec 12 '18 at 22:44
  • Separately, `14 * 25` is 350 not 375. I think `mult(0, Y, Y)` should be `mult(0, _, 0)`. – Dunes Dec 12 '18 at 22:45
  • @Dunes: yes... Unfortunately I did not check the semantics :) – Willem Van Onsem Dec 12 '18 at 22:46
  • I vaguely recall there was a time when, as an extension, SWI-Prolog would allow you to declare mathematical functions for the right side of `is/2`, but that time is long past and it is not ISO to be able to do so. – Daniel Lyons Dec 12 '18 at 22:47
  • I figured the mult(0, Y, Y) problem out. @WillemVanOnsem thank you, if I were to add a '!' before the 'Z is Y + Z1' would it remove the false that it returns also? – Ellis Thompson Dec 12 '18 at 22:49
  • @EllisThompson: yes, like `mult(0, _, Y) :- !`. Although if you later would generalize the predicate to make it multidirecitonal, this can have unwanted side-effects. – Willem Van Onsem Dec 12 '18 at 22:50
  • multidirectional being? – Ellis Thompson Dec 12 '18 at 22:51
  • @DanielLyons: yes, based on [this answer](https://stackoverflow.com/questions/28854812/implementing-user-defined-arithmetic-functions) it is - given of course the answer is correct, but I'm quite confident, not standardized. – Willem Van Onsem Dec 12 '18 at 22:51
  • @EllisThompson: that you can later query for `mult(X, 3, 15)` for example to obtian `X=5`. In Prolog, most predicates can be queried in several directions. For example `length(L, N)` can be used to query for the length of a list (like `length([1,4], N)`), but also validate the length (`length([1,4], 2)`) or generate a list of length 2 (`length(L, 2)`), or enumerate over lists with different lengths (`length(L, N)`). – Willem Van Onsem Dec 12 '18 at 22:51