I am new to using arithmetic in Prolog.
I’ve done a few small programs, but mostly involving logic. I am trying to implement a function that will return true
The syntax is a bit off: normally a clause has a head like foo(X, Y, Z)
, then an arrow (:-
), followed by a body. That body normally does not contain any arrows :-
. So the second arrow :-
makes not much sense.
Secondly in Prolog predicates have no input or output, a predicate is true
or false
(well it can also error, or got stuck into an infinite loop, but that is typically behavior we want to avoid). It communicates answers by unifying variables. For example a call sameSeqDiffs([3, 5, 7, 9], X)
. can succeed by unifying X
with 2
, and then the predicate - given it is implemented correctly - will return true.
.
In order to design a predicate, on typically first aims to come up with an inductive definition: a definition that consists out of one or more base cases, and one or more "recursive" cases (where the predicate is defined by parts of itself).
For example here we can say:
(base case) For a list of exactly two elements
[X, Y]
, the predicatesameSeqDiffs([X, Y], D)
holds, givenD
is the difference betweenY
andX
.
In Prolog this will look like:
sameSeqDiffs([X, Y], D) :-
___.
(with the ___
to be filled in).
Now for the inductive case we can define a sameSeqDiffs/2
in terms of itself, although not with the same parameters of course. In mathematics, one sometimes defines a function f such that for example f(i) = 2×f(i-1); with for example f(0) = 1 as base. We can in a similar way define an inductive case for sameSeqDiffs/2
:
(inductive case) For a list of more than two elements, all elements in the list have the same difference, given the first two elements have a difference
D
, and in the list of elements except the first element, all elements have that differenceD
as well.
In Prolog this will look like:
sameSeqDiffs([X, Y, Z|T], D) :-
___,
sameSeqDiffs(___, ___).
A common mistake people who start programming in Prolog make is they think that, like it is common in many programming languages, Prolog add semantics to certain functors.
For example one can think that A - 1
will decrement A
. For Prolog this is however just -(A, 1)
, it is not minus, or anything else, just a functor. As a result Prolog will not evaluate such expressions. So if you write X = A - 1
, then X
is just X = -(A,1)
.
Then how can we perform numerical operations? Prolog systems have a predicate is/2
, that evaluates the right hand side by attaching semantics to the right hand side. So the is/2
predicate will interpret this (+)/2
, (-)/2
, etc. functors ((+)/2
as plus, (-)/2
as minus, etc.).
So we can evaluate an expression like:
A = 4, is(X, A - 1).
and then X
will be set to 3
, not 4-1
. Prolog also allows to write the is
infix, like:
A = 4, X is A - 1.
Here you will need this to calculate the difference between two elements.
You were very close with your second attempt. It should have been
samediffs( [X, Y | Rest], Result):-
Result is Y - X,
samediffs( [Y | Rest], Result).
And you don't even need "to split the first two elements from the list". This will take care of itself.
How? Simple: calling samediffs( List, D)
, on the first entry into the predicate, the not yet instantiated D = Result
will be instantiated to the calculated difference between the second and the first element in the list by the call Result is Y - X
.
On each subsequent entry into the predicate, which is to say, for each subsequent pair of elements X
, Y
in the list, the call Result is Y - X
will calculate the difference for that pair, and will check the numerical equality for it and Result
which at this point holds the previously calculated value.
In case they aren't equal, the predicate will fail.
In case they are, the recursion will continue.
The only thing missing is the base case for this recursion:
samediffs( [_], _Result).
samediffs( [], _Result).
In case it was a singleton (or even empty) list all along, this will leave the differences argument _Result
uninstantiated. It can be interpreted as a checking predicate, in such a case. There's certainly no unequal differences between elements in a singleton (or even more so, empty) list.
In general, ......
recursion(A, B):- base_case( A, B).
recursion( Thing, NewThing):-
combined( Thing, Shell, Core),
recursion( Core, NewCore),
combined( NewThing, Shell, NewCore).
...... Recursion!