## My Extensible Calculator

An alternative title for this post might be **My Turing Complete Calculator**. As I mentioned previously, I was inspired to become a
Lisper after reading Paul Graham's Revenge of the Nerds. In
that essay Graham mentioned that he uses the Lisp REPL as a desktop
calculator. That made a lot of sense to me because I hate using a
mouse to deal with the typical GUI calculator and things like `bc`

are
too hard to remember how to use unless you run them every day.

So I started using the Scheme REPL as a calculator. It was a bit of a
pain to fire up Scheme every time I wanted to make some calculations
but no more so than calling up `bc`

or a GUI calculator. Then one
day, for reasons long forgotten, I needed to calculate the harmonic
series, H_{n} = 1/1 + 1/2 + 1/3 + … + 1/n, for various values of *n*.
It suddenly occurred to me that the Scheme REPL was actually an
extensible calculator in that I could add any function I wanted even
if it wasn't built in. Since then, I've accumulated these functions
in a scheme file that gets loaded by a script called `calc`

. Here's
the code for `calc`

.

#! /usr/local/bin/guile -e repl !# (use-modules (ice-9 readline)) (load "/Users/jcs/bin/calc.scm") (activate-readline) (define repl (lambda x ;;to disappear command line arguments (let ((exp (readline "calc--> "))) (if (or (eof-object? exp) (string=? exp "bye")) (format #t "bye\n") (begin (catch #t (lambda () (format #t "~a\n" (eval (read (open-input-string exp)) (interaction-environment)))) (lambda (key . args) (format #t "~s: ~s\n" key args))) (repl))))))

The home grown REPL is there because I first did this under PLT Scheme, which had a REPL function you could call. When I switched to Guile, which doesn't have one, I just rolled my own.

The other part of the calculator is my collection of add-on functions. Here's the help function that lists those functions.

(define help (lambda () (for-each (lambda (f) (display f) (newline)) '("! n: n!" "c->f t: convert celsius to Farenheit" "combo m n: combination of m things n at a time" "coprime? n m: are n and m relatively prime?" "dec->hex d: convert the decimal number d to hexidecimal" "digits n: number of digits in the number n" "f->c t: convert Farenheit to celsius" "factor n: factor the number n" "fibs n: list the first n Fibonacci numbers" "hex->dec h: convert hex number h (#xhhhh) to decinal" "hn n: harmonic sum of n terms" "lg x: an alias for log2" "log2 x: logarithm base 2 of x" "modinv n p: find the mod p inverse of a (p prime)" "next-larger-prime n: find smallest prime >= n" "prime? n: is n prime?" "primes n: list the first n primes" "simpson f a b n: apply Simpson's rule to f between a and b" )) 'Done))

Every time I find myself needing some function that I don't have, I
merely code it up in Scheme and add it to `calc.scm`

.

I can call `calc`

from the command line, of course, but I almost never
do. Instead I start it in an `ansi-term`

under Emacs. I have that
action bound to `C-c c`

so that it's easy to pop into the calculator
whenever I need it. That's especially convenient since, like most
Emacs users, I always have Emacs running.

hi jcs, would be great to some screenshot of a session.

ReplyDeletei find using REPL in nested syntax lang to be a problem. e.g. say you want to calculate 3+4/5+2.

Don't you have to write like (+ 3 (/ 4 5) 2)?

or, does it support algebraic like "3+4/5+2"? but then, when you need some function such as sqare root etc, it's a problem again. How you solve that?

Xah,

ReplyDeleteYes you have to use standard (parenthesized) notation but I don't mind that at all. In some cases it's even better: for instance 1+2+4+5+9+10 would be (+ 1 2 4 5 9 10) which is easier (at least for me) to type. If I need the square root of 2, say, it's just (sqrt 2). How is that any harder than sqrt(2)? But the best thing is that it's extensible. If I need some new function that's not already built in, I just write it and add it to me file of functions.

If you don't like the lisp syntax, you could do the same thing with, say, python and have infix notation. As usual, the power and extensibility of Emacs means you can have it your way, whatever that way is.

This is my first time i visit here and I found so many interesting stuff in your blog especially it's discussion, thank you.

ReplyDeletecalculator