Based to the comments above and the fact that you're still Prolog beginner, I will try to explain some things that may not be very clear:
What are variables in Prolog and what is instantiation ?
In Prolog variables are whatever starts with Capital letters and the anonymous variables _
(just an underscore defines an anonymous variable which is a variable without a name). Variables in Prolog means means that they could represent anything a list, a string, an atom (...whatever), when you use a new variable it is not instantiated which means it could represent anything but right now it does represent- it hasn't being bind
with a list, atom... When you want to instantiate a variable you could do that:
- by using unification
=/2
for example X = 2
unifies-instantiates variable X with number 2.
- or by using
is/2
only for expressions like X is 2
does the same as above.
Very important is that when a variable in Prolog is instantiated we know what it represents and we can not change for example X =2, X = 3 will fail!!
since we know that X binds with 2 and we try to reinstantiate with 3 which fails.
What is =/2, is/2, and what's the difference between these two prediactes ??
is/2
:it returns the result of an arithmetical expression to an uninstantiated variable. This means that you can use it like:
X is 2, or X is mod(Z,Y)
but it is important to notice here that whatever is in the expression MUST be instantiated
if it is a variable like Y and Z previously. If the expression is not instantiated you will get an instantiation error
as in your case. Even simple things like 2 is X+1
will fail with instantiation error if X is not instantiated so don't expect with is/2 to return something like X=1
in previous case which is an obvious answer (but not unique so it would be inconsistent!). Also arithmetic expression means that you could not use something like X is []
, to unify X with empty list this would of course fail, so in order to do that use =/2
, described below.
=/2
:This is used to unify a variable (or in general a term) with another term. For example you could use it like: X = 2
to unify X with number 2, or X = [1, 2]
to unify X with a list or X = 1 + 2
which will not give 3 but just the term 1 + 2, the last two examples I think clearly shows the difference with is/2
.
But then how could we use arithmetic expressions like is/2 but more relational ?
The answer here is library CLPFD
(as recommended by @lurker in the comments) all you have to do is place :- use_module(library(clpfd)).
in your file and use the operator #=
instead of is/2
. Now you could try X #= 2
, which will instantiate variable X with number 2 or even 2 #= 1 + X
and now you get no instantiation error but you get the answer X = 1
, so you can see that it is now much more relational.
Now to the question...
I agree with @Boris neutral should not be carried through recursion but declared with the function. Though I will try to keep up with your answer-attempt:
First of all when try to consult a file with your code in swi prolog I get:
Warning: /Users/Desktop/reduce.pl:1:
Singleton variables: [F,NEUTRAL,EE]
This says variables F,Neutral,EE are singleton it this clause:
reduce([],F,NEUTRAL,EE).
As you can see you don't use anywhere in this clause for example variable F.
To my opinion the reduce predicate should fail with empty list since you can't do any operation with the neutral element and a function which takes two parameters. So as base case I would define using a list with just one element:
reduce([H],F,NEUTRAL,EE):-
Goal =.. [F, H, NEUTRAL, EE],
call(Goal).
Then as explained above rules like:
add(N1,NEUTRAL, EE) :- EE is EE + N1 + NEUTRAL.
will cause instantiation error since in the EE is var and you can't use it in is/2 but even if it was instantiated you couldn't also use it because as explained EE is EE + whatever
will not work because you try to reinstantiate a variable..!!
In order to fix it I would try to use another variable for the next recursion and then calculate the result like:
reduce([H],F,NEUTRAL,EE):-
Goal =.. [F, H, NEUTRAL, EE],
call(Goal).
reduce([H|T],F,NEUTRAL,EE) :-
reduce(T,F,NEUTRAL,EE1),
Goal =.. [F, H, EE1, EE],
call(Goal).
(Here added a new variable EE1 do the recursive call and then calculate
the result based on the Result of EE1 that will be returned
from recursive call.)
Now let's test it:
?- reduce([1,2,3,4],add,0,L).
L = 10 ;
false.
?- reduce([1,2,3,4],mult,1,L).
L = 24 ;
false.
One more thing when it gave you the result L=10
by pressing ";" we ask for more answers and returns false since there are no possible answers. The false or true things are just a way that Prolog's toplevel informs you if the predicate succeeds or fails.For example:
?- reduce([1,2,3,4],mult,1,24).
true ;
false.
This states that the above is true and when we ask if there are any other ways that this could be true it returns false...
So that's it, after all Prolog doesn't seems to be that bad :) (according to your description).
As an exercise you could try to write the same using CLPFD in order to be more relational and be able to answer queries like: reduce(L,mult,1,24).
and maybe @Boris's idea about not carrying the neutral all the way through recursion.
EDIT
According to @false suggestion and useful comments I will write a second way using call/N which is clearly better(see comments...):
reduce([H],F,NEUTRAL,EE):-
call(F, H, NEUTRAL, EE).
reduce([H|T],F,NEUTRAL,EE) :-
reduce(T,F,NEUTRAL,EE1),
call(F, H, EE1, EE).