> without resorting to lookup tables!
I don’t see how that makes a difference. You can always replace a lookup table by code, for example:
a = [832, 54, 743]
vs
func a(i) =
if i = 0 return 832
if i = 1 return 54
return 743
The classic example of this are the definitions of
cons,
car and
cdr in SICP as lambdas:
(define (cons x y)
(lambda (m) (m x y)))
(define (car z)
(z (lambda (p q) p)))
(define (cdr z)
(z (lambda (p q) q)))
See
https://stackoverflow.com/a/21769444 for an explanation.
For pure functions taking finite inputs, the reverse is possible, too. For example, you can define and on booleans as a 2 × 2 array
and = [[false, false], [false, true]]
and then do a
and[x, y] lookup to evaluate it. That’s why some functional languages (for example scala) do not make a distinction between array indexing, hash table lookups, and function calls. After all, they all are mathematical functions taking a single value and producing one.
I think I would judge solutions not on avoiding lookup tables, but on size of the encoding and, for programs that produce equal size encodings, the total number of bytes in the programs, using “less is better” as criterion for both.