pub struct Context<N: Num> {
pub vars: HashMap<String, Term<N>>,
pub funcs: HashMap<String, Rc<dyn Func<N>>>,
pub cfg: Config,
}Expand description
A context holds values for variables and functions to be used in expressions. It is useful for both
parsing and evaluation expressions. During parsing, all names will be treated as variables unless
present in the Context the expression is being parsed with as functions at the time. The default
context (created with new()) contains basic functions and constants such as sin, pi, etc,
as well as the default configuration.
Contexts are used differently with Terms and Expressions. With Terms, more decisions are left
up to the user. Terms can be parsed with a reference to a context and evaluated with a reference
to a context. They never store any contextual information themselves. Expressions can be parsed
with an instance of a Context and will then store that Context within them. They can still be
evaluated with a reference to any other Context.
To define a custom variable, use set_var. It takes anything that implements Into<Term>, so you
can pass in just an f64 if you want.
let mut context: Context<f64> = Context::new();
context.set_var("x", 4.0);
let expr = Expression::parse_ctx("4x", context).unwrap();
let res = expr.eval(); // Ok(Answer::Single(16.0))A custom function is anything that implements the func::Func trait. There’s a
blanket impl of this trait allowing you to pass in any closure with the signature
Fn(&[Term<Num>], &Context<Num>) -> Calculation<Num>. You can also pass in a struct that implements Func manually
if you want more flexibility. The Func trait is just one method with the same signature previously
mentioned. Defining a custom function will most often look like this.
let mut context: Context<f64> = Context::new();
context.set_func("sum", |args: &[Term<f64>], ctx: &Context<f64>| -> Calculation<f64> {
if args.is_empty() { return Err(MathError::IncorrectArguments) }
let mut sum = 0.0;
for arg in args {
let a = arg.eval_ctx(ctx)?;
match a {
Answer::Single(n) => sum += n,
Answer::Multiple(ns) => {
for n in ns {
sum += n;
}
}
}
}
Ok(Answer::Single(sum))
});
let expr = Expression::parse_ctx("sum(5, 6, 7, 8)", context).unwrap();
let res = expr.eval(); // Ok(Answer::Single(26.0))The first argument of a custom function definition is a slice of Terms, which are the arguments
passed to the functions. The second argument is a reference to the Context the equation is being
evaluated with. It’s important to remember to evaluate any arguments you receive with the reference
to the Context you received with Term::eval_ctx(). If the function is given arguments in an
incorrect way, return a MathError::IncorrectArguments. If any errors occur during evaluation, you
can try to find a MathError variant that fits or return MathError::Other.
§Builtin
§Constants
- pi
- e
- i
§Functions
- sin
- cos
- tan
- asin
- acos
- atan
- atant (atan2)
- floor
- ceil
- round
- sqrt
- max
- min
Fields§
§vars: HashMap<String, Term<N>>HashMap of variables
funcs: HashMap<String, Rc<dyn Func<N>>>HashMap of functions
cfg: ConfigThe configuration used when evaluating expressions
Implementations§
Source§impl<N: Num + 'static> Context<N>
impl<N: Num + 'static> Context<N>
Sourcepub fn new() -> Self
pub fn new() -> Self
Returns a default Context
Examples found in repository?
8fn main() {
9 // A context holds data that can be used in an expression
10 let mut context: Context<f64> = Context::new();
11 // Add a variable "x" to the context with the value 5.4
12 context.set_var("x", 5.4);
13 // Add a function "sum" to the context that returns the sum of it's arguments. A closure is passed
14 // in that takes twp arguments: args: &[Term], which is a slice if the arguments passed into the
15 // function, and ctx: &Context, which is a reference to the context which the expression is being
16 // evaluated with. The item passed in can be anything that implements the `Func` trait. There exists
17 // a blanket impl for Fn(&[Term], &Context) -> Calculation which allows you to pass in closures in
18 // that format.
19 context.set_func(
20 "sum",
21 |args: &[Term<f64>], ctx: &Context<f64>| -> Calculation<f64> {
22 if args.len() < 1 {
23 return Err(MathError::IncorrectArguments);
24 };
25
26 let mut sum = Answer::Single(0.0);
27 for arg in args {
28 let b = arg.eval_ctx(ctx)?;
29 sum = sum.op(&b, |a, b| Num::add(a, b, ctx))?;
30 }
31
32 Ok(sum)
33 },
34 );
35
36 let raw = "2 * sum(x, 7, 400)";
37 // The expression needs to be parsed with the context in order do decide if some names are functions
38 // or variables.
39 let expr = Expression::parse_ctx(raw, context).unwrap();
40 // The expression also needs to be evaluated with a context. This context can be different than the
41 // one it was parsed with, but if it is missing something that is necessary for evaluation the
42 // evaluation will fail.
43 println!("{} = {}", raw, expr.eval().unwrap())
44}Sourcepub fn set_var<T: Into<Term<N>>>(&mut self, name: &str, val: T)
pub fn set_var<T: Into<Term<N>>>(&mut self, name: &str, val: T)
Add a variable definition to the context, replacing any existing one with the same name
Examples found in repository?
8fn main() {
9 // A context holds data that can be used in an expression
10 let mut context: Context<f64> = Context::new();
11 // Add a variable "x" to the context with the value 5.4
12 context.set_var("x", 5.4);
13 // Add a function "sum" to the context that returns the sum of it's arguments. A closure is passed
14 // in that takes twp arguments: args: &[Term], which is a slice if the arguments passed into the
15 // function, and ctx: &Context, which is a reference to the context which the expression is being
16 // evaluated with. The item passed in can be anything that implements the `Func` trait. There exists
17 // a blanket impl for Fn(&[Term], &Context) -> Calculation which allows you to pass in closures in
18 // that format.
19 context.set_func(
20 "sum",
21 |args: &[Term<f64>], ctx: &Context<f64>| -> Calculation<f64> {
22 if args.len() < 1 {
23 return Err(MathError::IncorrectArguments);
24 };
25
26 let mut sum = Answer::Single(0.0);
27 for arg in args {
28 let b = arg.eval_ctx(ctx)?;
29 sum = sum.op(&b, |a, b| Num::add(a, b, ctx))?;
30 }
31
32 Ok(sum)
33 },
34 );
35
36 let raw = "2 * sum(x, 7, 400)";
37 // The expression needs to be parsed with the context in order do decide if some names are functions
38 // or variables.
39 let expr = Expression::parse_ctx(raw, context).unwrap();
40 // The expression also needs to be evaluated with a context. This context can be different than the
41 // one it was parsed with, but if it is missing something that is necessary for evaluation the
42 // evaluation will fail.
43 println!("{} = {}", raw, expr.eval().unwrap())
44}Sourcepub fn set_func<F: Func<N> + 'static>(&mut self, name: &str, func: F)
pub fn set_func<F: Func<N> + 'static>(&mut self, name: &str, func: F)
Add a function definition to the context, replacing any existing one with the same name
Examples found in repository?
8fn main() {
9 // A context holds data that can be used in an expression
10 let mut context: Context<f64> = Context::new();
11 // Add a variable "x" to the context with the value 5.4
12 context.set_var("x", 5.4);
13 // Add a function "sum" to the context that returns the sum of it's arguments. A closure is passed
14 // in that takes twp arguments: args: &[Term], which is a slice if the arguments passed into the
15 // function, and ctx: &Context, which is a reference to the context which the expression is being
16 // evaluated with. The item passed in can be anything that implements the `Func` trait. There exists
17 // a blanket impl for Fn(&[Term], &Context) -> Calculation which allows you to pass in closures in
18 // that format.
19 context.set_func(
20 "sum",
21 |args: &[Term<f64>], ctx: &Context<f64>| -> Calculation<f64> {
22 if args.len() < 1 {
23 return Err(MathError::IncorrectArguments);
24 };
25
26 let mut sum = Answer::Single(0.0);
27 for arg in args {
28 let b = arg.eval_ctx(ctx)?;
29 sum = sum.op(&b, |a, b| Num::add(a, b, ctx))?;
30 }
31
32 Ok(sum)
33 },
34 );
35
36 let raw = "2 * sum(x, 7, 400)";
37 // The expression needs to be parsed with the context in order do decide if some names are functions
38 // or variables.
39 let expr = Expression::parse_ctx(raw, context).unwrap();
40 // The expression also needs to be evaluated with a context. This context can be different than the
41 // one it was parsed with, but if it is missing something that is necessary for evaluation the
42 // evaluation will fail.
43 println!("{} = {}", raw, expr.eval().unwrap())
44}