UNPKG

2.98 kBJavaScriptView Raw
1/**
2 * The expression parser of math.js has support for letting functions
3 * parse and evaluate arguments themselves, instead of calling them with
4 * evaluated arguments.
5 *
6 * By adding a property `raw` with value true to a function, the function
7 * will be invoked with unevaluated arguments, allowing the function
8 * to process the arguments in a customized way.
9 */
10const math = require('../../index')
11
12/**
13 * Calculate the numeric integration of a function
14 * @param {Function} f
15 * @param {number} start
16 * @param {number} end
17 * @param {number} [step=0.01]
18 */
19function integrate (f, start, end, step) {
20 let total = 0
21 step = step || 0.01
22 for (let x = start; x < end; x += step) {
23 total += f(x + step / 2) * step
24 }
25 return total
26}
27
28/**
29 * A transformation for the integrate function. This transformation will be
30 * invoked when the function is used via the expression parser of math.js.
31 *
32 * Syntax:
33 *
34 * integrate(integrand, variable, start, end)
35 * integrate(integrand, variable, start, end, step)
36 *
37 * Usage:
38 *
39 * math.eval('integrate(2*x, x, 0, 2)')
40 * math.eval('integrate(2*x, x, 0, 2, 0.01)')
41 *
42 * @param {Array.<math.expression.node.Node>} args
43 * Expects the following arguments: [f, x, start, end, step]
44 * @param {Object} math
45 * @param {Object} [scope]
46 */
47integrate.transform = function (args, math, scope) {
48 // determine the variable name
49 if (!args[1].isSymbolNode) {
50 throw new Error('Second argument must be a symbol')
51 }
52 const variable = args[1].name
53
54 // evaluate start, end, and step
55 const start = args[2].compile().eval(scope)
56 const end = args[3].compile().eval(scope)
57 const step = args[4] && args[4].compile().eval(scope) // step is optional
58
59 // create a new scope, linked to the provided scope. We use this new scope
60 // to apply the variable.
61 const fnScope = Object.create(scope)
62
63 // construct a function which evaluates the first parameter f after applying
64 // a value for parameter x.
65 const fnCode = args[0].compile()
66 const f = function (x) {
67 fnScope[variable] = x
68 return fnCode.eval(fnScope)
69 }
70
71 // execute the integration
72 return integrate(f, start, end, step)
73}
74
75// mark the transform function with a "rawArgs" property, so it will be called
76// with uncompiled, unevaluated arguments.
77integrate.transform.rawArgs = true
78
79// import the function into math.js. Raw functions must be imported in the
80// math namespace, they can't be used via `eval(scope)`.
81math.import({
82 integrate: integrate
83})
84
85// use the function in JavaScript
86function f (x) {
87 return math.pow(x, 0.5)
88}
89console.log(math.integrate(f, 0, 1)) // outputs 0.6667254718034714
90
91// use the function via the expression parser
92console.log(math.eval('integrate(x^0.5, x, 0, 1)')) // outputs 0.6667254718034714
93
94// use the function via the expression parser (2)
95let scope = {}
96math.eval('f(x) = 2 * x', scope)
97console.log(math.eval('integrate(f(x), x, 0, 2)', scope)) // outputs 4.000000000000003