Numberto has new features!
In previous post I wrote about simple clojure library numberto for experiments with numbers.
New version of numberto has a bunch of new features.
Expressions package provides two functions: eval-infix to evaluate infix expression
and infix->prefix to build prefix lisp-style expression from mathematical notation.
Let’s give it alias for simplicity
(def e eval-infix)
Evaluate simple math expression
(e "1+2") => 3
or more complex
(e "1+2*(3-4/2)") => 3
handle priorities
(e "2+2*2") => 6
and left/right associativity
(e "1024/2/2/2/2") => 64
(e "2^3^4") => 2417851639229258349412352N
Oh, what’s this? Long numbers? Sure, ratios and floats supported as well
(e "1/3") => 1/3
(e "1.1/0.9") => 1.2222222222222223
Unary operations
(e "(-1)^100") => 1
functions and symbols
(e "sin(e) + sqrt(pi)") => 2.183235141408425
vararg functions
(e "sum(1,2,3,sum())/max(1,2)") => 3
You can also provide bindings for unknown functions and symbols
(e "factorial(n)/20"
{:bindings
{"factorial" #(reduce *' (range 1 (inc %)))
"n" 10}})
=> 181440
Worth to mention that you can easily redefine existing
or define your own new unary, binary operations, functions
and symbols. Just add additional properties to eval-infix
;; return current time in millis
(e "now()" {:bindings {"now" #(.getTime (java.util.Date.))}}) => some long number
;; override priorities
(e "1+2*3" {:binary-ops {"+" {:function + :priority 100}}}) => 9
infix->prefix has exactly the same functionality, but it builds prefix expression instead.
(infix->prefix "(1+2)*3-(4/avg(3,5)-sum(1))")
=>
"(- (* (+ 1 2) 3) (- (/ 4 (avg 3 5)) (sum 1)))"
It can be useful if you googled some formula but bored to translate it manually to clojure.
For example, take the Simpson’s rule

(infix->prefix "(b-a)/6*(f(a)+4*f((a+b)/2)+f(b))")
=>
"(* (/ (- b a) 6) (+ (+ (f a) (* 4 (f (/ (+ a b) 2)))) (f b)))"
Would be good to try instaparse for such purpose, but I decided to use custom implementation using standard Shunting-yard algorithm. Just couple of hacks added to handle unaries and vararg functions. Code is awful. If you really want to dig in - run debug mode.
(binding [*DEBUG* true]
(e "1+2"))
| No way to define non-prefix unary operation, like “10!” or “ | 10 | ” |
(* (* 1 2) 3) instead of (* 1 2 3)(e "1+"), some not (e "1+()1). I still working on this, but it is not critical.Here is the puzzle:
You have four numbers [3, 4, 5, 6].
You have four binary operations [+, -, *, /] and parentheses ()How to insert operations between numbers to get number 42?
Hah, that simple 3*4 + 5*6 = 42
Ok, get 42, but you forced to use one division /.
Not so obvious?
(solve-insert-ops-num [3 4 5 6] 42) =>
([42N "3+45-6"] [42N "3/4*56"] [42N "3*4+5*6"])
If you use solve-insert-ops function it gives all possible values can be obtained by inserting operations between numbers.
(solve-insert-ops [3 4 5 6]) => ;; long list
Default implementation uses 4 basic operations, no parens and no restrictions. Instead, you can override options
to use parens, specify level
(solve-insert-ops-num [3 4 5 6] 42 {:parens 1}) =>
([42N "3+45-6"] [42N "(3+45)-6"] [42N "3+(45-6)"]
[42N "3/4*56"] [42N "(3/4)*56"] [42N "3/(4/56)"]
[42N "3*4+5*6"] [42N "(3*4)+5*6"] [42N "3*4+(5*6)"])
limit some operations
(solve-insert-ops-num [3 4 5 6] 42 {:rules [[:max "*" 1]]}) =>
([42N "3+45-6"] [42N "3/4*56"])
:max, :min, :max-in-a-row, :min-in-a-row options are supported.
Add new operations (supported by expressions package)
(solve-insert-ops-num [3 4 5 6] 80
{:ops ["+" "-" "*" "/" "^"]
:rules [[:max "^" 1]]}) =>
([80N "3^4+5-6"])
Keep in mind, always limit time consuming operations (like ^) as it builds all possible permutations and you can wait your answer forever.
There are also couple of new interesting things, like
getting digits of pi, e, sqrt(n), ratio numbers up to desired level and other. Check it out