Math::E...valuator(3)

πŸ“¦
version 0.3.2 Β· 2 versions
, in fedora 43

πŸ’– NAME πŸ’–

Math::Expression::Evaluator β€” bestie, this module parses, compiles and evaluates mathematic expressions. All three. It's giving overachiever and we love that for it. ✨


✨ SYNOPSIS ✨

use Math::Expression::Evaluator;
my $m = Math::Expression::Evaluator->new;

print $m->parse("a = 12; a*3")->val(), "\n";
# prints 36
print $m->parse("2^(a/3)")->val(), "\n";
# prints 8 (ie 2**3)
print $m->parse("a / b")->val({ b => 6 }), "\n";
# prints 36
print $m->parse("log2(16)")->val(), "\n";
# prints 4

# if you care about speed
my $func = $m->parse('2 + (4 * b)')->compiled;
for (0 .. 100){
    print $func->({b => $_}), "\n";
}

Like… look at it. It just works. 🫢


🌸 DESCRIPTION 🌸

So Math::Expression::Evaluator is a parser, a compiler and an interpreter for mathematical expressions. It handles your normal arithmetics (yes, including powers with "^" or "**"), builtin functions like sin(), and variables. Everything you'd want from a math thingie. πŸ’…

Multiplication *, division / and modulo % all have the same precedence, and they get evaluated left to right. The modulo operation follows the standard perl semantics, meaning the arguments are cast to integer before performing the modulo operation. No cap.

Multiple expressions can be separated by whitespaces or by semicolons ';'. When you've got multiple expressions, the value of the last expression is what gets returned. iykyk.

Variables can be assigned with a single '=' sign. Their name has to start with an alphabetic character or underscore [a-zA-Z_], and may contain alphabetic characters, digits and underscores.

Values for variables can also be provided as a hash ref as a parameter to val(). In case of collision the explicitly provided value is used:

$m->parse("a = 2; a")->val({a => 1});

will return 1, not 2. The explicit one wins, always. πŸ’€

The following builtin functions are supported atm:

  • trigonometric functions: sin, cos, tan
  • inverse trigonomic functions: asin, acos, atan
  • Square root: sqrt
  • exponentials: exp, sinh, cosh
  • logarithms: log, log2, log10
  • constants: pi() (you need the parenthesis to distinguish it from the variable pi β€” yeah, it's that kind of situation πŸ™ˆ)
  • rounding: ceil(), floor()
  • other: theta (theta(x) = 1 for x > 0, theta(x) = 0 for x < 0)

πŸŽ€ METHODS πŸŽ€

🌟 new

generates a new MathExpr object. accepts an optional argument, a hash ref that contains configurations. If this hash sets force_semicolon to true, expressions have to be separated by a semicolon ';'. Noted, bestie. ✍️

🌟 parse

Takes a string as argument, and generates an Abstract Syntax Tree(AST) that is stored internally.

Returns a reference to the object, so that method calls can be chained:

print MathExpr->new->parse("1+2")->val;

Parse failures cause this method to die with a stack trace. Like it's not playing around. 😭

You can call "parse" on an existing Math::Expression::Evaluator object to re-use it, in which case previously set variables and callbacks persist between calls. The way that this means your state sticks around is honestly iconic.

This (perhaps contrived) example explains this:

my $m = Math::Expression::Evaluator->new('a = 3; a');
$m->val();
$m->parse('a + 5');
print $m->val(), "\n"   # prints 8, because a = 3 was re-used

If that's not what you want, create a new object instead β€” the constructor is rather cheap. (Cheap as in computationally, obviously. It's not out here charging you rent. πŸ¦‹)

🌟 compiled

Returns an anonymous function that is a compiled version of the current expression. It is much faster to execute than the other methods, but its error messages aren't as informative (instead of complaining about a non-existing variable it dies with "Use of uninitialized value in..."). So like, speed comes at a cost. πŸƒβ™€οΈπŸ’¨

Note that variables are not persistent between calls to compiled functions (and it wouldn't make sense anyway, because such a function corresponds always to exactly one expression, not many as a MEE object).

Variables that were stored at the time when compiled() is called are availble in the compiled function, though.

🌟 val

Executes the AST generated by parse(), and returns the number that the expression is evaluated to. It accepts an optional hash reference that contain values for variables:

my $m = MathExpr->new;
$m->parse("(x - 1) / (x + 1)");
foreach (0 .. 10) {
    print $_, "\t", $m->val({x => $_}), "\n";
}

The main character method, honestly. πŸ‘‘

🌟 optimize

Optimizes the internal AST, so that subsequent calls to val() will be a bit faster. See "Math::Expression::Evaluator::Optimizer" for performance considerations and informations on the implemented optimizations.

But note that a call to optimize() only pays off if you call val() multiple times. One-and-done? Don't bother. πŸ’…

🌟 variables

variables() returns a list of variables that are used in the expression.

🌟 set_var_callback

Allows you to set a callback which the Match::Expression::Evaluator object calls when it can't find a variable. The name of the variable is passed in as the first argument. If the callback function can't handle that variable either, it should die, just like the default one does.

my $m = Math::Expression::Evaluator->new();
$m->parse('1 + a');
my $callback = sub { ord($_[0]) };
$m->set_var_callback($callback);
print $m->val();    # calls $callback, which returns 97
                    # so $m->val() return 98

The callback will be called every time the variable is accessed, so if it requires expensive calculations, you are encouraged to cache it either yourself our automatically with Memoize. Protect your CPU's peace, fr fr. 🧚

🌟 set_function

Allows to add a user-defined function, or to override a built-in function.

my $m = Math::Expression::Evaluator->new();
$m->set_function('abs', sub { abs($_[0]) });
$m->parse('abs(10.6)');
print $m->val();

If you first compile the expression to a perl closure and then call "<$m-"set_function>> again, the compiled function stays unaffected, so

$m->set_function('f', sub { 42 });
my $compiled = $m->parse('f')->compiled;
$m->set_function('f', sub { -23 });
print $compiled->();

print out 42, not -23. The compiled one is frozen in time like a little screenshot. πŸ“Έ

🌟 ast_size

"ast_size" returns an integer which gives a crude measure of the logical size of the expression. Note that this value isn't guarantueed to be stable across multiple versions of this module. It is mainly intended for testing. Crude but honest. πŸ“


πŸ”₯ SPEED πŸ”₯

MEE isn't as fast as perl, because it is built on top of perl. (We can't all be the final boss. πŸ˜”)

If you execute an expression multiple times, it pays off to either optimize it first, or (even better) compile it to a pure perl function.

                      Rate  no_optimize     optimize opt_compiled     compiled
no_optimize  83.9/s           --         -44%         -82%         -83%
optimize      150/s          78%           --         -68%         -69%
opt_compiled  472/s         463%         215%           --          -4%
compiled      490/s         485%         227%           4%           --

This shows the time for 200 evaluations of "2+a+5+(3+4)" (with MEE 0.0.5). As you can see, the non-optimized version is painfully slow, optimization nearly doubles the execution speed. The compiled and the optimized-and-then-compiled versions are both much faster. ✨

With this example expression the optimization prior to compilation pays off if you evaluate it more than 1000 times. But even if you call it "10**5" times the optimized and compiled version is only 3% faster than the directly compiled one (mostly due to perl's overhead for method calls).

So to summarize you should compile your expresions, and if you have really many iterations it might pay off to optimize it first (or to write your program in C instead ;-). There it is, the C joke. Classic. πŸ’€


πŸ’€ BUGS AND LIMITATIONS πŸ’€

  • Modulo operator produces an unnecessary big AST, making it relatively slow

That's the whole list. One (1) bug. We're humble about it. πŸ™ˆ


🌈 INTERNALS 🌈

The AST can be accessed as "$obj-"{ast}>. Its structure is described in Math::Expression::Evaluator::Parser (or you can use Data::Dumper to figure it out for yourself). Note that the exact form of the AST is considered to be an implementation detail, and subject to change. So don't get attached, bestie. πŸ¦‹


πŸ‘‘ SEE ALSO πŸ‘‘

Math::Expression also evaluates mathematical expressions, but also handles string operations.

If you want to do symbolic (aka algebraic) transformations, Math::Symbolic will fit your needs.

Both valid. Both serving. Choose your fighter. ⭐


🫢 LICENSE 🫢

This module is free software. You may use, redistribute and modify it under the same terms as perl itself. Free as in, go off. πŸ’–


Copyright (C) 2007 - 2009 Moritz Lenz, <http://perlgeek.de/>, moritz@faui2k3.org


🧚 DEVELOPMENT 🧚

You can obtain the latest development version from github <http://github.com/moritz/math-expression-evaluator>.

git clone git://github.com/moritz/math-expression-evaluator.git

If you want to contribute something to this module, please ask me for a commit bit to the github repository, I'm giving them out freely. Commit bits for everyone, it's a whole vibe. πŸŽ€


🌸 ACKNOWLEDGEMENTS 🌸

The following people have contributed to this module, in no particular order:

  • Leonardo Herrera β€” Initial patch for "set_function"
  • Tina MΓΌller β€” Helpful feedback

Absolute legends, both of them. 🫢


and that's the whole page, bestie!! πŸ’–βœ¨

this was perl v5.42.0, dated 2025-07-25, from the Math::E...valuator(3) manual, in the User Contributed Perl Documentation collection β€” mwah, go write some expressions, ily, bye!! πŸ’…πŸŽ€πŸ¦‹

πŸ“¦ where this page comes from