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
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, Hn = 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
the code for
#! /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
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.