Maths question

Pages: 12
Is there any fundamental difference between how a constant and a variable are evaluated in maths? I'm writing a library for evaluating expressions where the user can register their own functions and variables, with some being set by default (most of the functions in math.h as well as π). Is it wise to handle constants and variables as if they are the same, or should I separate them?

I remember someone in a maths class getting an answer wrong because they treated π as if it were a variable instead of treating it as if it were a value, so I tried to reproduce it by seeing if (4π)2 = 16π and it doesn't (according to WolframAlpha), which indicates to me that π is treated like a variable - (4x)2 = 16x2, not 16x.

And for another thing, what is the order of operations for functions like logarithms, sin, etc.? Are they evaluated left-to-right?
And for another thing, what is the order of operations for functions like logarithms, sin, etc.? Are they evaluated left-to-right?

I think so.
Is it wise to handle constants and variables as if they are the same, or should I separate them?

My opinion, separate them.
Last edited on
There's no point separating them if they're evaluated the same way, though.
And for another thing, what is the order of operations for functions like logarithms, sin, etc.? Are they evaluated left-to-right?


They're handled inner-to-outer, if you're using brackets (which, to be honest, you should). If you're not, they're evaluated right-to-left, because the result of the "inner"/leftmost is the argument for the next.

E.g. d = sin log cos x²
a = x²
b = cos(a)
c = log(b)
d = sin(c);


A value and a variable are the same; the variable is just a placeholder. In your example,
(4π)² = 16π
, you'd be wrong no matter what 'n' is. n is inside the brackets, thus it has to be squared, just like you would square the 'x' in (4x)², or the '2' in (4*2)².

I'm guessing your memory is pulled out of context. Sometimes, 'n' has a placeholder is all you need. If you don't need to know the actual value, for example, which is in strong contrast to the common "solve for x" when we see the variable x. This is often the case in proofs, or any form of generalized expression, as in "for any value of 'n'": 'n' has no value; it's a placeholder. It signifies any and every value (unless explicitly bounded).

You can treat them differently, and you probably should. If you get a set of equations in x, y, z, then you'll end up trying to find the appropriate values for those three variables. If you get x, y, z and n, chances are you'll have to find values for x,y and z, expressed in 'n'. (e.g. x = 5n, y = 3/2n, z = 5^n).
Last edited on
@chrisname: I mean, separate them so there will be no such thing as:
5 = 2x
So they're not completely evaluated the same way...
Last edited on
Gaminic wrote:
They're handled inner-to-outer, if you're using brackets (which, to be honest, you should). If you're not, they're evaluated right-to-left, because the result of the "inner"/leftmost is the argument for the next.

I'm going to require parentheses for functions.

Gaminic wrote:
You can treat them differently, and you probably should

(also, @EssGeEich)
I didn't think about variables being defined in terms of other variables, I was thinking of them as just being numbers, but actually I'll store them as strings and evaluate them when they're used (that way, if x changes, then y = 2x will change with it).

[edit] Somehow I left one of my sentences unfinished.
Last edited on
Gaminic is most probably right, his logic seems the most infallible. and the substitution part was pretty darn nice!!!
I'd treat constants like variable with special (empty?) name. You know you can summarize variables with equal names

1 + x + 1 + x -> 1() + 1(x) + 1() + 1(x)

This way you don't need to differentiate
summation from 0 to n, x^i.

for all n, element of the set integers, (4n)^2=16^2*n^2

An even integer is represented as 2n for some integer n.

Solving an equation, sin(x) = 0 x = (0, 2PI*n, PI*n). If a range of values for n isn't given it usually means for all integers.

Usually when you use n, your representing some integer, all integers, or a range of integers.

n is not a good choice for a constant, or non integer variable.
Last edited on
You should be able to distinguish between three basic kinds of objects:
1. Constants: e.g. 5, pi, and used-defined values. Variables in programming are a close analogy, only they have state.
2. Bound variables. In the expression "sum with i from 1 to 10: i", i is a bound variable. They can be compared to actual (as opposed to formal) parameters in programming; again, minus the state.
3. Free variables. These are typically used to make generalizations. For example, in "sum with i from 1 to n: i", n is a free variable because it's meaning is undefined. A hypothetical algorithm to simplify mathematical expressions should simplify to "(n^2+n)/2" or something equivalent. n is left untouched throughout the computation, except when it can be safely simplified away. Free variables are sort of like formal parameters; they're a way of saying "this expression is valid for more than one value of this object".
Last edited on
For constants (I'm referring specifically to named constants here, not numbers), I'm going to store them in a list and just substitute the values in. Functions are stored in a similar list with a token, a number of parameters, and a pointer to the C function to call when the token is encountered. Then the value that the C function returns will be substituted into the expression. For variables, I was just going to let users specify a token and an expression, then that expression will be substituted into the main expression (inside parentheses) and evaluated along with it wherever the token appears.

@helios,
I didn't really think about simplifying expressions, just evaluating already existing expressions to get a single number.
Last edited on
There's no point separating them if they're evaluated the same way, though.


If I'm understanding the question, I would say separate them. Some operations do different things for constants and variables -- most notably, differentiation where terms that are just a constant are dropped. Integration always adds the constant C to account for this (and it also has special properties).

No other examples come to mind at the moment...

Of course, you don't have to support those operations...
Last edited on
I was planning to support differentiation and integration. And yeah, I've decided to separate constants and variables.
differentiation where terms that are just a constant are dropped


Again: this isn't a reason to handle variables differently. In differentiation, every variable that isn't the differenter is treated as a constant. That doesn't make it a constant though, and labelling it as such is foolishness.

The only constants are numbers like pi and e, but those will be hardcoded anyway. All other variables are the same, even if not all of them are analyzed (e.g. an equation with 'n' and 'x', "solve for x", get a result in x = f(n)).
How can I describe an operator like |x| which surrounds its parameter as opposed to an operator like '+' which goes in between them (infix) or an operator like unary '-' which goes before it (prefix)?
Last edited on
A function, maybe? Just like sin, dy/dx, etc.
[edit] I'm just calling it "ntokens".

Well, there's |x| which is the same as abs(x) and then there's ||v|| which gets the magnitude of a vector. I can't really treat them as functions because functions are a single token: sin, log, etc. whereas these have a start token and an end token. I thought I could store all the tokens in a union like this:
1
2
3
4
5
6
7
8
9
10
11
struct matheval_operator {
	union {
		char*		en;
		struct {
			char*	left;
			char*	right;	
		} ens;
	} tok;
	int			nparams;
	int			ntokens;
};

which is inside a structure. But I don't know what to call the member of the structure that determines whether to use tok.en or tok.ens.
Last edited on
I can't really treat them as functions because functions are a single token


I disagree. I do treat math operators as functions, and so does c++. This is how I would do it, on the parsing scheme I use (shown by helios on this forum):
Introduce a new token, "||". Then do the following substitution rules:

"|", "|" -> "||"

"||", "Expression", "||" -> "VectorMagnitude(Expression)".

[Edit:]And this is how you do absolute value:
"|", "Expression", "|" -> "AbsoluteValue(Expression)"

Let's do an example: we will simplify the expressions ||x|, ||x||, |||x|||

Example ||x|:
Token Stack:
1
2
3
4
5
6
"|"    (no rule to apply read next token)
"|","|"    -> "||"
"||"    (no rule to apply read next token)
"||", "x"    (no rule to apply read next token)
"||", "x", "|"    (no more tokens to load and rules to apply: exit parsing)
the token stack did not reduce to a single expression. Display an error message to the user saying what it reduced to instead.


Example ||x||:
Token Stack:
1
2
3
4
5
6
7
8
9
10
"|"    (no rule to apply read next token)
"|","|"    -> "||"
"||"    (no rule to apply read next token)
"||", "x"    (no rule to apply read next token)
"||", "x", "|"    (no rule to apply read next token)
"||", "x", "|", "|"    -> "||", "x",       "||"     (rules are applied always to the top of the stack)
"||", "x", "||"    -> "VectorMagnitude(x)"
(no more tokens to load and rules to apply: exit parsing)
The result is a single expression, display success message!
 


Example |||x|||:
Token Stack:
1
2
3
4
5
6
7
8
9
10
11
12
13
"|"    (no rule to apply read next token)
"|","|"    -> "||"
"||"    (no rule to apply read next token)
"||", "|"    (no rule to apply read next token)
"||", "|", "x"     (no rule to apply read next token)
"||", "|", "x","|"    -> "||", "AbsoluteValue(x)"
"||", "AbsoluteValue(x)"     (no rule to apply read next token)
"||", "AbsoluteValue(x)", "|"     (no rule to apply read next token)
"||", "AbsoluteValue(x)", "|", "|"    -> "||", "AbsoluteValue(x)", "||"        
"||", "AbsoluteValue(x)", "||"  -> "VectorMagnitude(AbsoluteValue(x))"
(no more tokens to load and rules to apply: exit parsing)
The result is a single expression, display success message!
 

Last edited on
Thanks for the examples. I will do it like that.
The problem with the |x| notation is that its nested variation is ambiguous in a context-free grammar.
Pages: 12