Problem: Given an unsigned integer numeral in a specified base in the range from two to thirty-six, determine the integer that it denotes.
An unsigned integer numeral in a given base is a string made up entirely of
digits of that base. It will be convenient to assume that the character
code is ASCII, although Scheme does not require this. The digits of a base
b that is in the range from to to ten are #\0
and the
b - 1 characters immediately following it in the character set; if
the base is greater than ten, its digits are #\0
through
#\9
followed by the first b - 10 letters of the
alphabet, without regard to case.
The idea of the algorithm is to traverse the numeral from left to right,
computing the value of each digit of the numeral separately by calling the
internally defined procedure digit-value
, and adding it to an
accumulator (total
) of which the value is multiplied by the
base on each iteration.
The procedure definition begins by introducing a sequence of local
variables, which are introduced mainly for legibility; then comes the
locally defined procedure, and finally the lambda-expression that denotes
the procedure to be named numeral-value
:
The converse operation of constructing the numeral in a given base for a given natural number is similar in structure, except that zero must be handled as a special case:(define numeral-value (let ((digit-value (let ((zero-code (char->integer #\0)) (nine-code (char->integer #\9)) (capital-A-code (char->integer #\A)) (capital-Z-code (char->integer #\Z)) (lower-case-a-code (char->integer #\a)) (lower-case-z-code (char->integer #\z))) (lambda (ch) (let ((code (char->integer ch))) (cond ((<= zero-code code nine-code) (- code zero-code)) ((<= capital-A-code code capital-Z-code) (+ 10 (- code capital-A-code))) ((<= lower-case-a-code code lower-case-z-code) (+ 10 (- code lower-case-a-code))) (else 0))))))) (lambda (numeral base) (let ((len (string-length numeral))) (let loop ((total 0) (index 0)) (if (= index len) total (loop (+ (* base total) (digit-value (string-ref numeral index))) (+ index 1))))))))
The composition of these procedures converts a numeral from one base to another:(define numeral-for (let ((digit-for (let ((zero-code (char->integer #\0)) (lower-case-a-code (char->integer #\a))) (lambda (n) (cond ((<= 0 n 9) (integer->char (+ n zero-code))) ((<= 10 n 35) (integer->char (+ n lower-case-a-code -10))) (else #\*)))))) (lambda (number base) (if (zero? number) "0" (let loop ((chars '()) (rest number)) (if (zero? rest) (apply string chars) (loop (cons (digit-for (remainder rest base)) chars) (quotient rest base))))))))
(define base-convert (lambda (numeral input-base output-base) (numeral-for (numeral-value numeral input-base) output-base)))
This document is available on the World Wide Web as
http://www.math.grin.edu/~stone/events/scheme-workshop/numerals.html