|
Posted by Richard Cornford on 07/09/05 17:02
^reaper^ wrote:
> Michael Winter wrote:
<snip>
>> I have seen other interesting uses for the eval function,
>> but even they weren't really necessary.
>
> I'm not sure what you mean by unnecessary. After all, there
> are generally several ways to implement code. And outside of
> gross coding practices (e.g., extensive use of globals and
> for-loops, to name but a couple), the tradeoffs are (or should
> be) the primary factor in the final decision.
>
> But anyway, here's one for you.
>
>:var period = new Array(
>: new MyObj( 'years','this.per * 12' ),
>: new MyObj( 'semi-annual',"this.per / 2' ),
>: new MyObj( 'quarters','this.per * 4' ),
>: new MyObj( 'months','this.per / 12' ),
>: new MyObj( 'days','this.per * 365' ),
>:)
>:var calcs = new Array(
>: new MyObj('fvLumpSum','pv*(Math.pow((1+r),nper))' ),
>: new MyObj('fvAnnuity','pmt*((Math.pow((1+r),nper)-1)/r)' ),
>: new MyObj('fv','eval(eqn[0].eqn) + (eval(eqn[1].eqn)*( 1+r*ltype))'
>: ),
>: new MyObj('arGrad',
>:'g*((Math.pow((1+r),nper)-r*nper-1)/(Math.pow(r,2)*Math.Pos((1+r),nper
)))'
>: ),
>: new MyObj('arGradPW',
>:'g*((Math.pow((1+r),nper)-(r*nper)-1)/(Math.pow(r,2)*math.pow((1+r),np
er)))'
>: ),
>: new MyObj('geoPW','A*(nper*Math.pow((1+r),-1))' ),
>: new MyObj('geoPWnG',
>:'A*((1-Math.pow((1+g),nper)*Math.pow((1+r),-nper))/(r-g))' ),
>: new MyObj('projRate','Math.log(mult)/Math.log(1+r)' )
>:)
>:function MyObj( nm, eq ){
>: this.name = nm;
>: this.eqn = eq;
>: this.per = 0;
>:}
>:function calculate(){
>: var r = parseFloat( document.all['rate'].value ) / 100;
>: var pv = parseFloat( document.all['principal'].value );
>: var pmt = parseFloat( document.all['payment'].value );
>: var interval = document.all['interval'].selectedIndex;
>: var kind = document.all['kind'].selectedIndex;
>: var g = document.all['guess'].value;
>: period[interval].per = document.all['calcs'].value;
>: var nper = eval( calcs[interval].eqn );
>: document.all[period[kind].name].innerText = eval( calcs[kind].eqn );
>:}
>
> As you can see, the above is quite simplistic. This example
> btw, was born out of a discussion involving an original 200+
> line script that was primarily if-then-else ad nauseum. Even
> in light of this rendition, wherein I am modeling a macro-like
> scenario, there is no doubt a more efficient way to implement
> the solution. And yes, I'm old school, so I tend to favor
> macros. Unfortunately, unlike compiled langs, we don't have the
> benefit of true macros (which are inlined at compile time).
> Nonetheless, rather than completely giving away the store here,
> I'll let you ponder this a bit of code, and even attempt to
> guess, if you wish, why I may have chosen the route that I did...
> my favoritism of macros notwithstanding. ^_~
In a functional programming language like javascript, where functions
are first class objects and may be anonymously created, passes around by
reference and called indirectly, most macro concepts are amenable to
implementation with functions.
Because the code above makes (indirect) reference to the identifiers g,
A, eqn and mult, none of which are defined above, it is impossible to
say whether it does make a case for its eval use or not. Their
significance and role in the code is unknown.
However, in considering the factors that might influence implementation
decisions the cost of maintenance is inevitably a significant factor.
And maintenance includes the cost of debugging (and when debugging is
not maintenance but instead initial development it doesn't become any
cheaper). The extra layer and indirection of eval use is a problem for
debugging because the error reports that browsers generate from evaled
code usually refer to the line of code in which the eval call appears
and so the exact code that produced the error is not apparent. In
addition the source code for the eval call is either a string literal or
a combination of string literals and variable values.
This is apparent in the code above where the misspelling of 'Math' in
")*math.pow((1+r),nper)))" has been overlooked. In raw javascript source
code, displayed in an editor with javascript-dedicated syntax
highlighting rules the misspelling might be immediately evident to the
author. Syntax highlighted as a string literal that error may go
unobserved for a considerable period of time, while debugging effort is
expended in other directions.
Even if syntax highlighting did not reveal an equivalent programming
error to the author running the result through a JS lint program would
result in 'math' being flagged as an undeclared global variable. So the
author could do a quick text search and locate the cause of the
potential problem. While code inside a string literal will not even be
recognised as code by a lit program.
Another less than obvious consequence of the indirection imposed by the
eval call is that the line:-
var nper = eval( calcs[interval].eqn );
- stands a very good chance (at least 3 in 4) of executing a line of
code that makes reference to the value of - nper - as an operand for a
math operator. As at the point of the evaluation of this line the -
nper - local variable has not yet been initialised its value will be
Undefined, and Undefined used in a math operation will invariable result
in the NaN numeric value.
In an implementation using indirect references to function objects to
achieve a macroing effect the - nper - value would be expected to be
being passed as an argument so its un-initialised state would be more
evident in the code using that function. E.G.:-
var nper = calcs[interval].eqn(nper, r, pv, pmt, g, A, eqn. mult);
- where it should be apparent that as this is the initial assignment
to - nper - its use in the arguments list will be passing an Undefined
value.
(alt.php removed from cross-posted groups (as this is OT there) and
comp.lang.javascript added)
Richard.
[Back to original message]
|