UNPKG

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