formulae v0.8.0 Formulae View Source

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

t()

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.

Evaluates the formula returning the result back.

Evaluates normalized representation of formula.

normalize(input) deprecated

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.)

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.

Link to this section Types

Specs

t() :: %atom(){
  formula: binary(),
  ast: nil | tuple(),
  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

Link to this function

bindings?(formula, bindings \\ [])

View Source
This function is deprecated. Use `Formulae.compile/1` and `%Formulae{}.variables` or `Formula.curry/2` instead.

Specs

bindings?(formula :: t() | binary() | tuple(), binding :: keyword()) ::
  keyword()

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
Link to this function

check(string, bindings \\ [])

View Source
This function is deprecated. Use `Formulae.eval/2` instead.

Specs

check(string :: binary(), bindings :: keyword()) :: boolean()

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

compile(t() | binary()) :: t()

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

compiled?(binary() | t()) :: boolean()

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
Link to this function

curry(input, binding \\ [], opts \\ [])

View Source

Specs

curry(input :: t() | binary(), binding :: keyword(), opts :: keyword()) :: t()

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"
Link to this function

eval(string, bindings \\ [])

View Source

Specs

eval(string :: binary(), bindings :: keyword()) :: term()

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
Link to this function

evaluate(input, binding \\ [], opts \\ [])

View Source
This function is deprecated. Use `Formulae.eval/2` instead.

Specs

evaluate(input :: binary() | tuple(), binding :: keyword(), opts :: keyword()) ::
  boolean() | no_return()

Evaluates normalized representation of formula.

Examples

iex> Formulae.evaluate(Formulae.unit("3 > 2"))
true

iex> Formulae.evaluate(Formulae.unit("3 < 2"))
false

iex> Formulae.evaluate(Formulae.unit("a < 2"), [a: 1])
true

iex> Formulae.evaluate(Formulae.unit("a > 2"), [a: 1])
false

iex> Formulae.evaluate(Formulae.unit("a < 2"), [])
** (Formulae.RunnerError) Formula failed to run (compile): incomplete binding to evaluate a formula, lacking: [:a].

iex> Formulae.evaluate(Formulae.unit("a + 2 = 3"), [a: 1])
true

iex> Formulae.evaluate(Formulae.unit("a + 2 = 3"), [a: 2])
false

iex> Formulae.evaluate(Formulae.unit(~S|a = "3"|), [a: "3"])
true

iex> Formulae.evaluate(Formulae.unit(~S|a = "3"|), [a: 3])
false

iex> Formulae.evaluate(Formulae.unit(~S|a = "3"|), [a: "hello"])
false

iex> Formulae.evaluate("a + 2 = 3", [a: 2])
false

iex> Formulae.evaluate(~S|a = "3"|, [a: "3"])
true

iex> Formulae.evaluate(Formulae.unit("a_b_c_490000 > 2"), [a_b_c_490000: 3])
true
This function is deprecated. Use `Formulae.compile/1` and `%Formulae{}.variables` instead.

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

purge(t() | binary()) :: :ok | {:error, :not_compiled} | {:error, :code_delete}

Purges and discards the module for the formula given (if exists.)

This function is deprecated. Use `Formulae.eval/2` instead.

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"]}}