Advanced Use Cases ################## .. highlight:: php .. _lazy-calc: Lazy Calculations ================= Calculator vs LazyCalculator ---------------------------- The default calculator has some advantages over the lazy calculator: * Works faster * Non-recursive calculation, not limited by call stack * Easier to debug missing variables and functions Still, lazy calculator is a must if your variables may be missing or you need to guard against error conditions. .. note:: Lazy functions and operators can still be used in the default calculator, just their arguments will be pre-calculated. LazyArgument ------------ ``Arokettu\ArithmeticParser\Argument\LazyArgument`` LazyArgument is an interface that wraps inner calculations. In ``LazyCalculator`` the inner calculations are not performed unless you call ``getValue()``. In ``Calculator`` it's just a wrapper for the float value that would be passed normally. Partial Execution ----------------- The most useful application of lazy calculator is partial execution:: addOperator(new Config\UnaryOperator( '?', function (LazyArgument $a) { try { return $a->getValue(); // if a value is defined, return it } catch (UndefinedVariableException) { return 0; // fall back to zero } }, lazy: true, )); $config->addOperator(new Config\BinaryOperator( '??', function (LazyArgument $a, LazyArgument $b) { try { return $a->getValue(); // if a value is defined, return it } catch (UndefinedVariableException) { return $b->getValue(); // fall back to the second argument } }, 1_000_000, // top priority lazy: true, )); var_dump(LazyCalculator::evaluate('log(a, b ?? e())', $config, a: 1024, b: 2)); // 10 var_dump(LazyCalculator::evaluate('log(a, b ?? e())', $config, a: 1024)); // 6.9314... // default config equivalent: var_dump(LazyCalculator::evaluate('log(a, if(defined(b), b, e()))', a: 1024)); // 6.9314... // Default calculator will still accept a lazy operation var_dump(Calculator::evaluate('log(a, b ?? e())', $config, a: 1024, b: 2)); // 10 // but the actual fallback won't work var_dump(Calculator::evaluate('log(a, b ?? e())', $config, a: 1024)); // UndefinedVariableException // Lazy unary was created specifically because of error handling possibility var_dump(LazyCalculator::evaluate('a? + b? + c?', $config)); // 0 var_dump(LazyCalculator::evaluate('a? + b? + c?', $config, a: 1, c: 3)); // 4 var_dump(LazyCalculator::evaluate('a? + b? + c?', $config, a: 1, b: 2, c: 3)); // 6 // Default calculator will still accept a lazy operation var_dump(Calculator::evaluate('a? + b? + c?', $config, a: 1, b: 2, c: 3)); // 6 // but the actual optional won't work var_dump(Calculator::evaluate('a? + b? + c?', $config)); // UndefinedVariableException Dynamic Functions ================= .. warning:: Calling parser and calculator with a different config objects is not supported unless only functions were added. Since, unlike operators, functions are not resolved by the parser, you can dynamically add missing functions before the actual calculation:: parse('log2(2048) + log3(27)'); $warnings = Validator::validate($parsed, $config, []); foreach ($warnings as $w) { // find the warning about missing functions if ($w instanceof Validator\MissingFunctionsWarning) { foreach ($w->missingFunctions as $f) { // add logarithm function based on base value in the name if (str_starts_with($f->normalizedName, 'LOG')) { $base = \intval(substr($f->normalizedName, 3)); $config->addFunctionFromCallable($f->name, fn ($a) => log($a, $base)); } } break; } } var_dump((new Calculator($parsed->operations, $config))->calc()); // 14