Abacus is a simple interactive calculator CLI with support for variables, lambdas, comparison checks, and math functions
ฮป abacus -h
abacus - a simple interactive calculator CLI with support for variables, lambdas, comparison checks, and math functions
v1.4.0
Usage: abacus [--no-color] [--allow-copy] [--strict] [--precision PRECISION] [--eval EVAL] [--import IMPORT] [--prompt-symbol PROMPT-SYMBOL] [--answer-vars ANSWER-VARS]
Options:
--no-color, -n disable color in output [default: false]
--allow-copy Ctrl-C will copy current expression (if present) or last answer instead of aborting [default: false]
--strict prohibit use of undefined lambdas and variables [default: false]
--precision PRECISION, -p PRECISION
precision for calculations [default: 64]
--eval EVAL, -e EVAL evaluate expression and exit
--import IMPORT, -i IMPORT
import statements from file and continue
--prompt-symbol PROMPT-SYMBOL
custom prompt symbol [default: > ]
--answer-vars ANSWER-VARS
custom last answer variable names [default: ans,answer]
--help, -h display this help and exit
--version display version and exit
yay -S abacus-git
go install github.com/viktordanov/abacus@latest
-
History of expressionsandTab completionof all math functions and defined variables -
Importing from file (-i --import)lets you keep variable and lambda definitions in a file and import it on load--import IMPORT, -i IMPORT import statements from file and continue > abacus -i definitions.a > DefinedLambda(5) ... -
Custom prompt symbol (--prompt-symbol)lets you use a custom string as a prompt prefix--prompt-symbol PROMPT-SYMBOL custom prompt symbol [default: > ] $ abacus --prompt-symbol "๐ฑ " ๐ฑ 2+2 4 ๐ฑ ... -
Evaluate and exit (-e --eval)lets you evaluate an expression and exit without entering REPL mode; Imports by-i --importare performed before-e --evalso they can be combined--eval EVAL, -e EVAL evaluate expression and exit > abacus -e "5+5" 10 -
All common operations
> 1+1 2 > 1-20 -19 > 5^0 / 20 0.05 > 2**(2+5) 128 > 10% 0.10 > 10 %% 3 # modulo operator 1 -
Variables
> d = 12.5 12.5 > d * 5 + 5 67.5 > a * 5 + 5 5Note: Undefined variables are equal to 0
-
Last answers are saved in the variables
ansandanswerby default> 4**5 1024 > ans 1024 -
Comparisons
<, ==, >, <=, >=> pi > phi true > 10 <=10 true > 2 == 0 false -
E, Pi, Phi
> e 2.7182818284590450907955982984276 > pi 3.1415926535897931159979634685442 > phi 1.6180339887498949025257388711907 -
Single arity functions:
- sqrt, cbrt, ln, log, log2, log10, floor, ceil, exp, sin, cos, tan, abs, round
-
Two arity functions (accept 2-tuples):
- round (number, digits of precision)
> round(1.123456789,4) 1.123- log (number, base)
> log(16,4) 2 -
N-arity functions (accept n-tuples):
- min, max, avg, from, until, reverse, nth
> d, f = 10, 20 (10, 20) > min(d, 4, -1, f, 0, 2) -1 > max(d, 4, -1, f, 0, 2) 20 > avg(d, 4, -1, f, 0, 2) 5.8333.. > Map__ = value,Fn -> Fn(value), Map__(value+1, Fn) > List = start, len, Fn -> until(Map__(start, Fn)[rec: len], len) > I = x -> x > List(1, 5, I) (1, 2, 3, 4, 5) > from(List(1, 5, I), 2) (3, 4, 5) > until(List(1, 5, I), 2) (1, 2) > nth(List(1, 5, I), 2) 3 > reverse(List(1, 5, I)) (5, 4, 3, 2, 1)
Note: from(List(1, 5, I), 2) is equivalent to from(1,2,3,4,5,2)
quitโ If a query includes quit, the program will terminate and the query will not be saved to the history fileansandanswerโ variables always contain the last computed numeric value (can be overriden with the--answer-varsargument)- The constants
e,pi, andphi
<LambdaName> = <arguments> -> <expression> // or
<LambdaName> = (<arguments>) => <expression> // or
<LambdaName> = <arguments> -> <expression>, <expression>, ...
Both variables and lambda placeholders/aliases can be provided as arguments:
Identity = x -> x
RunFnOnX = x, Fn -> Fn(x)
Note:
- Lambda names begin with a capital letter
- Parentheses around the arguments are optional, except when no variables are to be provided, e.g.
F = () -> 5+5 - Lambdas can return multiple values - a tuple.
- Both
->and=>can be used between the lambda variables and the expressions tuple.
Identity = x -> x
RunFnOnX = x, Fn -> Fn(x)
Area = a, b => a*b
Hypothenuse = (a,b) -> sqrt(a**2+b**2)
ToRad = deg -> deg * pi / 180
<LambdaName>(<parameters>)
> Identity = x -> x
> Identity(2)
2
> Identity()
expected 1 parameter
> UndefinedLambda()
0
Note:
- undefined lambdas return 0 by default like undefined variables
Lambdas can use global variables and constants and will default to global variables if
a variable in the lambda expression isn't in the arguments tuple. The same applies to lambda aliases.
> Add = x, y -> (x + y) * not_found
> Add(5, 6)
0
> not_found = 1
> Add(5, 6)
11
> ApplyFn = x, Fn -> Fn(x)
> ApplyFn(12, Test)
0
> Test = x -> x*2
> ApplyFn(12, Test)
24
Lambdas can be recursive but only if explicitly told when called.
> Factorial = x -> x * Factorial(x - 1)
> Factorial(10)
recursion is disabled
To specify recursion parameters use [] after the lambda call.
F(value)[rec=10, last=x*10, stop=x < 5, mem: false]
recโ Expression (evaluated globally), Default: 0- specifies the maximum number of times the lambda can call itself during the evaluation of the current expression;
lastโ Expression (evaluated by the lambda), Default: 0- specifies the expression which the last lambda automatically evalates when
recis reached or whenstopis true;
- specifies the expression which the last lambda automatically evalates when
stopโ BoolExpression (evaluated by the lambda)- a boolean expression which can use the lambda's variables; if true, the lambda returns
lastand stops recurring;
- a boolean expression which can use the lambda's variables; if true, the lambda returns
memโ BoolExpression (evaluated globally), Default: false- whether or not to utilize memoization for the current expression (useful when using recursion).
Either = or : may be used between the parameter name and the value.
> Factorial(10)[rec: 10]
0
> Factorial(10)[rec:10, last=1]
3628800
> Factorial(10)[rec:10, last:1, stop: x == 5]
30240 // 10 ร 9 ร 8 ร 7 ร 6
If we define a new lambda Count = x -> x,Count(x-1) which returns a tuple we can observe how the value of x changes.
> Count(10)[rec:10]
(10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
> Count(10)[rec:10, last:100]
(10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 100)
> Count(10)[rec:4, last:x*5]
(10, 9, 8, 7, 30)
> Count(10)[rec:10, last:x, stop: x == 5]
(10, 9, 8, 7, 6, 5)
> Fib = x -> Fib(x-1) + Fib(x-2)
> Map_ = value -> Fn(value), Map_(value-1)
> Map = value,length->Map_(value)[rec:length-1,last:Fn(value)]
> Fn = x -> Fib(x)[rec:1e6,last:1,stop:x<3]
> Map(10,10)
(55, 34, 21, 13, 8, 5, 3, 2, 1, 1)
> until(Map(10,10), 5)
(55, 34, 21, 13, 8)
> reverse(Map(10,10))
(1, 1, 2, 3, 5, 8, 13, 21, 34, 55)
> from(reverse(Map(10,10)), 1)
(1, 2, 3, 5, 8, 13, 21, 34, 55)
> from(reverse(Map(10,10)), 5)
(8, 13, 21, 34, 55)
> nth(from(reverse(Map(10,10)),5),2)
21
// If we attempted Map(20,10) it would take a while to compute because
Fib(20) branches out 2^n times.
> Map(20,10)
(6765, 4181, 2584, 1597, 987, 610, 377, 233, 144, 89)
// But due to the nature of the recursive Fibonacci algorithm, a lot
of the same function calls are made which means we can drammatically
speed up execution by caching computations.
> Fn = x -> Fib(x)[rec:1e6, last:1, stop:x<3, mem: true]
> Map(200,1)
280571172992510140037611932413038677189525
// Try it for yourself
- Add full feature list
- Write tests
- Base tests
- Simple benchmark of a complicated expression
- Improve tests
- Refactor to depend less on other packages
- Implement most single arity functions with
*big.Floatfor improved precision
