<<< Continued from part 1 >>>
So far, so good. The scheme is fairly consistent, simple, and
understandable. But if we look more closely, we discover that there are
some strange things going on:
'(counteval(0)) $B"*(B counteval(0) OK
ev(counteval(0)) $B"*(B counteval(0) OK
ev('(counteval(0))) $B"*(B counteval(1) ?!
similarly
'('4) $B"*(B '4 OK
ev('('4)) $B"*(B 4 ?!
and also
count:100$
counteval('count) $B"*(B counteval(count+1) OK
ev(counteval('count)) $B"*(B counteval(101) ?!
and
ev(counteval(0),noeval) $B"*(B counteval(0)
OK, no evaluation
ev(counteval(count),noeval) $B"*(B counteval(100)
?! variable eval$B!G(Bd, but not function
Well... it turns out that symbols and quoted expressions are
special-cased. They get pre-evaluated in ev - even with noeval. This is
a feature, to allow things like ev(%,numer) or ev(d23/d24,numer) instead
of requiring ev(''%,numer) and ev(''(d23/d24),numer). But the result is
that the semantics are a mess.
Here is another, more obscure, peculiarity:
ev(rectform(%e^%i)) => cos(1)+%i*sin(1) OK
ev(%e^%i,rectform) => cos(1)+%i*sin(1) OK (rectform is an evfun)
ev('rectform(%e^%i)) => 'rectform(%e^%i) OK
ev('rectform(%e^%i),rectform) => 'rectform(%e^%i) ?!
ev('rectform(%e^%i),'rectform) => cos(1)+%i*sin(1) Phew!
The problem here is that a function is an evfun, you can$B!G(Bt use the
normal syntax for de-nounifying it. This is an example of peculiar
interactions.
ev does a host of other clever things which make its semantics very very
obscure. Most of these clever things make sense in some specific (and
maybe even common) context, but they don$B!G(Bt generalize well, and they
often interact in peculiar ways. It only makes things worse that the
documentation describes what ev does operationally - at a sort of
program design language level - but it never actually discusses the
concepts.
So let me try to summarize conceptually (some of) what ev does:
* defines the evaluation context of its main argument (setting
flags, binding variables, etc.)
ev(%pi/2,numer=true)
ev(%pi/2,numer)
short form of previous
ev(sqrt(x^2-1),x=2)
* substitutes global variable values
ev(%)
* controls evaluation (none, one, multiple, infinite)
ev(...,eval)
de-nounifies
ev('integrate(x,x),integrate)
ev('integrate(x,x),nouns)
* performs substitutions
ev(a*x+b=y,x=(y-b)/a)
* applies functions to the expression (ratsimp, ...)
ev((x-1)*(x+1)+1,ratsimp)
So why is all this functionality in one messy function, instead of in
multiple clean functions? Convenience. The command-line syntax x,y lets
you access all sorts of functionality at the cost of typing just one
comma (,), and no strain on the memory to remember special-purpose
functions. It is, after all, easier to write
asin(1)
%,numer
and expect it to do the right thing rather than
asin(1)
block([numer:true],''%)
(though float(%) is probably what you really want) and
d23,q^2=r^4,integrate
rather than
apply_nouns(subst(q^2,r^4,d23),integrate)
(actually, apply_nouns: (a) is not currently implemented and (b) isn$B!G(Bt
documented to allow specifying particular nouns to apply).
I think it would be worthwhile to think of a better scheme for all this.
Something easy to use for the simple cases, but consistent in the more
complex cases.
In the meantime, if you really want just eval, and nothing else, you
can't use ev or even ev(...,eval); instead, use the internal Maxima
evaluator directly, ?meval(...).
-s