Refactor redundancy and add clarity in computations with let-bindings.
Consider the following procedure that contains some repetitious code.
;;; (letter->number ch) -> either integer? boolean?
;;; ch : char?
;;; Converts ch to the corresponding number in the English alphabet.
;;; (1 for #\a or #\A, 2 for #\b or #\B, etc.). Returns false (#f)
;;; if ch is not a letter in the English alphabet.
(define letter->number
(lambda (ch)
(cond
[(and (<= (char->integer #\a) (char->integer ch))
(<= (char->integer ch) (char->integer #\z)))
(+ 1 (- (char->integer ch) 97))]
[(<= (char->integer #\a) (+ (char->integer ch) 32) (char->integer #\z))
(+ 1 (- (+ (char->integer ch) 32) 97))]
[else #f])))
(map letter->number (list #\a #\b #\c #\d #\e))
(1 2 3 4 5)
(map letter->number (list #\A #\B #\C #\D #\E))
(1 2 3 4 5)
(map letter->number (list #\X #\Y #\Z))
(24 25 26)
(letter->number #\.)
"Not a letter"
As you know, we should avoid redundant computations and magic numbers. Using
local bindings (let or let*), remove the redundant computations and magic
numbers from letter->number. You need not change the primary structure
of the cond, but you should not compute the same value twice.
Note: In case you didn’t know, 32 is the result of subtracting the
collating sequence number of #\A from the collating sequence number
of #\a. By adding 32, we switch from uppercase to lowercase. And
97 seems to be the collating sequence number of #a, but we probably
shouldn’t count on that.