View Source Formulae (formulae v0.10.3)
A set of functions to deal with analytical formulae.
The typical way of using this module would be to call Formulae.compile/1
on the binary representing the string.
iex|1 ▶ f = Formulae.compile "a + :math.sin(3.14 * div(b, 2)) - c"
%Formulae{
ast: {:-, [line: 1],
[
{:+, [line: 1],
[
{:a, [line: 1], nil},
{{:., [line: 1], [:math, :sin]}, [line: 1],
[{:*, [line: 1], [3.14, {:div, [line: 1], [{:b, [line: 1], nil}, 2]}]}]}
]},
{:c, [line: 1], nil}
]},
eval: &:"Elixir.Formulae.a + :math.sin(3.14 * div(b, 2)) - c".eval/1,
formula: "a + :math.sin(3.14 * div(b, 2)) - c",
module: :"Elixir.Formulae.a + :math.sin(3.14 * div(b, 2)) - c",
variables: [:a, :b, :c]
}
Now the formula is compiled and might be invoked by calling Formulae.eval/2
passing a formula and bindings. First call to eval/2
would lazily compile
the module if needed.
iex|2 ▶ f.eval.(a: 3, b: 4, c: 2)
0.9968146982068622
The formulae might be curried.
iex|3 ▶ Formulae.curry(f, a: 3, b: 4)
%Formulae{
ast: ...,
eval: &:"Elixir.Formulae.3 + :math.sin(3.14 * div(4, 2)) - c".eval/1,
formula: "3 + :math.sin(3.14 * div(4, 2)) - c",
module: :"Elixir.Formulae.3 + :math.sin(3.14 * div(4, 2)) - c",
variables: [:c]
}
Link to this section Summary
Types
The formulae is internally represented as struct, exposing the original binary representing the formula, AST, the module this formula was compiled into, variables (bindings) this formula has and the evaluator, which is the function of arity one, accepting the bindings as a keyword list and returning the result of this formula application.
Functions
Returns the binding this formula requires.
Revalidates the formula with bindings given. Returns true if the formula
strictly evaluates to true
, false
otherwise. Compiles the formula
before evaluation if needed.
Generated clauses for n ∈ [1..42]
to be used with dynamic number
Compiles the formula into module.
Checks whether the formula was already compiled into module.
Curries the formula by substituting the known bindings into it.
Checks whether the formula was already compiled into module.
Similar to compiled?/1
, but returns what Code.ensure_compiled/1
returns.
Evaluates the formula returning the result back.
Evaluates the formula returning the result back; throws in a case of unseccessful processing.
Evaluates normalized representation of formula.
Returns a normalized representation for the formula given.
Generated clauses for n ∈ [1..12]
to be used with dynamic number
Purges and discards the module for the formula given (if exists.)
Link to this section Types
Specs
t() :: %{ __struct__: atom(), formula: binary(), ast: nil | Macro.t(), guard: nil | Macro.t(), module: nil | atom(), variables: nil | [atom()], eval: nil | (keyword() -> any()) }
The formulae is internally represented as struct, exposing the original binary representing the formula, AST, the module this formula was compiled into, variables (bindings) this formula has and the evaluator, which is the function of arity one, accepting the bindings as a keyword list and returning the result of this formula application.
Link to this section Functions
Specs
Returns the binding this formula requires.
Examples
iex> "a > 5" |> Formulae.bindings?
~w|a|a
iex> ":math.sin(a / (3.14 * b)) > c" |> Formulae.bindings?
~w|a b c|a
iex> "a + b * 4 - :math.pow(c, 2) / d > 1.0 * e" |> Formulae.bindings?
~w|a b c d e|a
Specs
Revalidates the formula with bindings given. Returns true if the formula
strictly evaluates to true
, false
otherwise. Compiles the formula
before evaluation if needed.
Specs
combinations(list :: list(), count :: non_neg_integer()) :: [list()]
Generated clauses for n ∈ [1..42]
to be used with dynamic number
Specs
Compiles the formula into module.
Examples:
iex> f = Formulae.compile("rem(a, 5) - b == 0")
iex> f.formula
"rem(a, 5) - b == 0"
iex> f.variables
[:a, :b]
iex> f.module
:"Elixir.Formulae.rem(a, 5) - b == 0"
iex> f.module.eval(a: 12, b: 2)
true
iex> f = Formulae.compile("rem(a, 5) + b == a")
iex> f.variables
[:a, :b]
iex> f.eval.(a: 7, b: 5)
true
iex> f.eval.(a: 7, b: 0)
false
Specs
Checks whether the formula was already compiled into module.
Typically one does not need to call this function, since this check would be nevertheless transparently performed before the evaluation.
Examples:
iex> Formulae.compiled?("foo > 42")
false
iex> Formulae.compile("foo > 42")
iex> Formulae.compiled?("foo > 42")
true
Specs
Curries the formula by substituting the known bindings into it.
Example
iex> Formulae.curry("(temp - foo * 4) > speed / 3.14", temp: 7, speed: 3.14).formula
"7 - foo * 4 > 3.14 / 3.14"
Specs
ensure_compiled(binary() | t(), options :: options()) :: {:module, module()} | {:error, :embedded | :badfile | :nofile | :on_load_failure | :unavailable}
Checks whether the formula was already compiled into module.
Similar to compiled?/1
, but returns what Code.ensure_compiled/1
returns.
Typically one does not need to call this function, since this check would be nevertheless transparently performed before the evaluation.
Examples:
iex> Formulae.ensure_compiled("bar > 42")
{:error, :nofile}
iex> Formulae.compile("bar > 42")
iex> Formulae.ensure_compiled("bar > 42")
{:module, :"Elixir.Formulae.bar > 42"}
Specs
Evaluates the formula returning the result back.
Examples:
iex> Formulae.eval("rem(a, 5) + rem(b, 4) == 0", a: 20, b: 20)
true
iex> Formulae.eval("rem(a, 5) == 0", a: 21)
false
iex> Formulae.eval("rem(a, 5) + rem(b, 4)", a: 21, b: 22)
3
Specs
Evaluates the formula returning the result back; throws in a case of unseccessful processing.
Examples:
iex> Formulae.eval!("rem(a, 5) == 0", a: 20)
true
iex> Formulae.eval!("rem(a, 5) == 0")
** (Formulae.RunnerError) Formula failed to run (compile): [:missing_arguments] wrong or incomplete eval call: [given_keys: [], expected_keys: [:a]].
Specs
evaluate(input :: binary() | tuple(), binding :: keyword(), opts :: keyword()) :: boolean() | no_return()
Evaluates normalized representation of formula.
Examples
iex> Formulae.eval("3 > 2")
true
iex> Formulae.eval("3 < 2")
false
iex> Formulae.eval("a < 2", a: 1)
true
iex> Formulae.eval("a > 2", a: 1)
false
iex> Formulae.eval("a < 2", [])
{:error, {:missing_arguments, [given_keys: [], expected_keys: [:a]]}}
iex> Formulae.eval!("a < 2", [])
** (Formulae.RunnerError) Formula failed to run (compile): [:missing_arguments] wrong or incomplete eval call: [given_keys: [], expected_keys: [:a]].
iex> Formulae.eval("a + 2 == 3", a: 1)
true
iex> Formulae.eval("a + 2 == 3", a: 2)
false
iex> Formulae.eval(~S|a == "3"|, a: "3")
true
iex> Formulae.eval(~S|a == "3"|, a: 3)
false
iex> Formulae.eval(~S|a == "3"|, a: "hello")
false
iex> Formulae.eval("a + 2 == 3", a: 2)
false
iex> Formulae.eval(~S|a == "3"|, a: "3")
true
iex> Formulae.eval("a_b_c_490000 > 2", a_b_c_490000: 3)
true
Returns a normalized representation for the formula given.
Specs
permutations(list :: list(), count :: non_neg_integer()) :: [list()]
Generated clauses for n ∈ [1..12]
to be used with dynamic number
Specs
Purges and discards the module for the formula given (if exists.)
Produces the normalized representation of formula. If the rho is
an instance of Integer
or Float
,
it’s left intact, otherwise it’s moved to the left side with negation.
Examples
iex > Formulae.unit("3 > 2")
{"3 > 2", {:>, [], [3, 2]}}
iex > Formulae.unit("3 - a > 2")
{"3 - a > 2", {:>, [], [{:-, [line: 1], [3, {:a, [line: 1], nil}]}, 2]}}
iex > Formulae.unit("3 > A + 2")
{"3 > a + 2",
{:>, [],
[{:-, [context: Formulae, import: Kernel],
[3, {:+, [line: 1], [{:a, [line: 1], nil}, 2]}]}, 0]}}
iex > Formulae.unit("3 >= a + 2")
{"3 >= a + 2",
{:>=, [],
[{:-, [context: Formulae, import: Kernel],
[3, {:+, [line: 1], [{:a, [line: 1], nil}, 2]}]}, 0]}}
iex > Formulae.unit("3 a > A + 2")
** (Formulae.SyntaxError) Formula [3 a > A + 2] syntax is incorrect (parsing): syntax error before: “a”.
iex > Formulae.unit("a + 2 = 3")
{"a + 2 = 3", {:==, [], [{:+, [line: 1], [{:a, [line: 1], nil}, 2]}, 3]}}
iex > Formulae.unit(~S|A = "3"|)
{"a = \"3\"", {:==, [], [{:a, [line: 1], nil}, "3"]}}