Evaluation issue using accumulators in Prolog to evaluate a polynomial

青春壹個敷衍的年華 提交于 2019-12-10 16:48:24

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!