问题
Background
I need to write a predicate eval(P,A,R), where:
P represents a list of polynomial coefficients, i.e. 1+2x+3x^2 is represented as [1,2,3].
A represents the value for X.
R is the result of the polynomial at X=A.
Example: eval([3,1,2],3,R) produces R = 24. *edit, previously incorrect example
I am trying to use accumulators from this article and example on Learn Prolog Now.
My Algorithm:
0. Initialize result and exponent variables to 0.
1. Take the head of the list.
2. Multiply the head of the list by A^(exponent).
3. Update result and exponent.
My code:
eval(P,A,R) :- accEval(P,A,0,0,R).
accEval(([H|T]),A,Pow,Accres,R) :-
Rnew is (Accres+H*(A**Pow)),
Pownew is Pow+1,
R = Accres,
accEval(T,A,Pownew,Rnew,Rnew). % *See below
accEval([],A,Pow,Accres,Accres).
% *Previously, when the second Rnew in this line was R instead, output was "no".
Produces trace:
| ?- eval([1,2],3,R).
1 1 Call: eval([1,2],3,_20) ?
2 2 Call: accEval([1,2],3,0,0,_20) ?
3 3 Call: _126 is 0+1*3**0 ?
3 3 Exit: 1.0 is 0+1*3**0 ?
4 3 Call: _157 is 0+1 ?
4 3 Exit: 1 is 0+1 ?
5 3 Call: accEval([2],3,1,1.0,1.0) ?
6 4 Call: _218 is 1.0+2*3**1 ?
6 4 Exit: 7.0 is 1.0+2*3**1 ?
7 4 Call: _249 is 1+1 ?
7 4 Exit: 2 is 1+1 ?
8 4 Call: accEval([],3,2,7.0,7.0) ?
8 4 Exit: accEval([],3,2,7.0,7.0) ? % We have the correct answer.
5 3 Exit: accEval([2],3,1,1.0,1.0) ? % Wait! What are you doing!?
2 2 Exit: accEval([1,2],3,0,0,0) ? % Why is this falling back out?
1 1 Exit: eval([1,2],3,0) ?
R = 0 % Incorrect. The answer should be 7.
As noted in my code, previous attempts have produced no value for R, instead producing "no" or in other implementations "yes".
Question
Why is the result lost and not carried back to the original call?
回答1:
The variable that is supposed to be unified with the accumulator value at the end of the recursion is already bound to a value. The way that you use accumulators is as follows:
- give the accumulator an initial value when you first "call" the predicate
- use the accumulator to pass the accumulated value down the recursive call
- unify your result with the accumulator in the end-of-recursion clause
As a rule of thumb, the Result variable in the head of the predicate is the same variable that is passed down to the recursive call. This way, once it gets unified with the actual result at the end (in the end-of-recursion clause), it will also be passed up out of the recursion to the caller.
回答2:
it's a bit simpler:
eval(P,A,R) :- accEval(P,A,0,0,R).
accEval(([H|T]),A,Pow,Accres,R) :-
Rnew is (Accres+H*(A**Pow)),
Pownew is Pow+1,
accEval(T,A,Pownew,Rnew,R). % *See below
accEval([],_A,_Pow,Accres,Accres).
but your example seems incorrect
?- eval([3,2,1],3,R).
R = 18.
and indeed 3+2*X+X^2 with X=3 yields 18
回答3:
A strategy here could be the so called Horner schema. In the Horner schema you don't need a power function (**)/2 or (^)/2. The Horner schema says:
a_0 + a_1*x + .. + a_n-1*x^n-1 + a_n*x^n =
a_0 + x*(a_1 + .. + x*(a_n-1 + x*a_n)..)
If you allow supplying the coefficents in reverse order [a_n,..,a_0] the Horner schema is readily implemented in Prolog as follows:
eval([C|L], X, R) :-
eval(L, C, X, R).
eval([C|L], A, X, R) :-
B is A*X+C,
eval(L, B, X, R).
eval([], A, _, A).
As a bonus the Horner scheme is numerically more stable. Here is an example run:
?- eval([2,1,3],3,R).
R = 24
来源:https://stackoverflow.com/questions/15259118/evaluation-issue-using-accumulators-in-prolog-to-evaluate-a-polynomial