Programming Languages and Paradigms

Suppose that your boss came to you with a new client, a grocer. This client would like you to write a program that verifies the UPC barcodes read by his scanner. It turns out that all legal UPC barcodes possess a distinctive arithmetic property used to catch errors.

If we call the *i*th digit of the barcode `d[i]`, then
there is a function *f* such that the sum of `f(d[i],i)`
for all the digits in a legal barcode is evenly divisible by 10.
The function f(d[i],i) is defined as:

- d[i], if i is odd
- 3 * d[i], if i is even

UPC barcodes are 0-based; that is, the first digit is considered the 0th digit.

No problem, you say. You write the following procedures to verify UPC barcode digits:

(define f (lambda (digit pos) (if (zero? (modulo pos 2)) (* 3 digit) digit)))

We'd like to apply `f` to each digit in a UPC and then add
up the results to see if the sum is divisible by 10.

This sounds like a great application formap-- except thatmaptakes aone-argument function. Thisfoperates onboththe digitandthe digit's position in the sequence. We need a version ofmapthat works with procedures of two arguments, the digit and its position in the list.Next week, we'll begin to study recursion in some detail, and you'll learn some powerful techniques that will help us implement such a function. For now, I'll do what any programmer would do: I'll define a procedure named

counted-mapthat does just this for us.The definition of

counted-mapis given in a source file that contains all of the code from this section. Feel free to study this procedure definition now if you'd like, but you are free to save it for after Exam 1. (self-verifying-numbers.rktis also part of the .zip file available for this session from the Session Summaries page.)

Once we have `counted-map`, writing a procedure to verify
UPC barcodes is pretty straightforward. We use
`counted-map` to apply `f` individually to each
digit in the list:

(counted-map f list-of-digits)

We then use `apply` to add up the list of values returned
by `counted-map`:

(apply + (counted-map f list-of-digits))

Then we need to find the remainder of dividing this value by 10,
using Racket's primitive procedure `modulo`:

(modulo (apply + (counted-map f list-of-digits)) 10)

And verify that the number is evenly divisible by 10, by checking
this result with `zero?`

(zero? (modulo (apply + (counted-map f list-of-digits)) 10))

[ **Note**: The `(zero? (modulo ... 10))` code is a
Racket idiom that resembles a similar idiom in Java and C:
`(x % 10) == 0` ]

That expression is the body of our procedure:

> (define valid-UPC? (lambda (list-of-digits) (zero? (modulo (apply + (counted-map f list-of-digits)) 10)))) > (valid-UPC? '(2 7 3 5 9 1 9 3 0 5)) #t > (valid-UPC? '(2 9 3 7 9 0 8 4 9 5)) #f > (valid-UPC? '(3 9 2 0 8 9 0 0 0 3)) #t

Time passes. Your boss is so proud of your concise solution that she shows it to another client, a bookstore employee who handles inventory. This client asks your boss to ask you to write a similar bit of code to verify ISBN numbers prior to processing special-request orders from his own clients.

While studying the topic further, you learn that ISBN numbers
also have a distinctive arithmetic property used to catch errors:
if we call the *i*th digit of the number d[i], then the sum
of `f(d[i],i)` for all the digits is evenly divisible by
11.

The only differences between a UPC code and an ISBN number in
this regard is that they use a different function `f` and
a different modulus `m`! For ISBN numbers, the function
`f(d[i],i)` is defined as `d[i] * i`, with 1 being
the position of the first digit.

You learn that there is an entire area work dealing with
**self-verifying numbers** that share this property, with
only the function `f` and the modulus `m` differing.
(Other numbers of this type are credit card numbers and the serial
numbers on US Postal Service money orders.)

When you re-examine your `valid-UPC?` procedure, you
realize now that you have hard-coded references to specific values
for `f` and `m` into your code. So, you could do
the same thing for ISBN numbers and write a procedure that works
in the same way:

> (define ISBN-f (lambda (digit pos) (* digit (+ pos 1)))) > (define valid-ISBN? (lambda (list-of-digits) (zero? (modulo (apply + (counted-map ISBN-f list-of-digits)) 11)))) > (valid-ISBN? '(2 7 3 5 9 1 9 3 0 5)) #f > (valid-ISBN? '(2 9 3 7 9 0 8 4 9 5)) #f > (valid-ISBN? '(0 8 9 8 1 5 4 6 4 2)) #t

Your procedures work fine, but they violate a basic principle of
programming: *"Say everything once and only once."* If you
have made a mistake in your code for the modular sum algorithm, or
if the algorithm changes for some reason, then you will have to
make the change in `verify-UPC` *and*
`verify-ISBN`. Even more important, when your boss later
asks you to implement solutions for validating credit card numbers
and USPS money orders, you will have to code the same algorithm
yet again!

But if both of these algorithms do exactly the same thing, only
with different values for `f` and `m`, why not make
`f` and `m` **parameters** to a procedure that
does the common work? If both of the arguments were numbers or
some other basic type, you would not think twice about doing this.
It would be the normal thing to do!

(define validate (lambda (f m list-of-digits) (zero? (modulo (apply + (counted-mapflist-of-digits))m))))) (define valid-UPC? (lambda (list-of-digits) (validate UPC-f 10 list-of-digits))) (define valid-ISBN? (lambda (list-of-digits) (validate ISBN-f 11 list-of-digits)))

This allows us to validate numbers, at the cost of an extra procedure call. In other languages, though, you might not be able to do more than this. Functions in most languages can return many kinds of values, but not procedures. But in Racket, a function is like any other value. Can we do better?

We can write a procedure that *produces a procedure as its
value*. Let's change `validate` into a procedure that
returns a validating procedure:

(define make-validator (lambda (f m) ; -------------------------------------------------------(lambda (list-of-digits) (zero? (modulo (apply + (counted-map; ------------------------------------------------------- ))flist-of-digits))m)))

You might think of `make-validator` as a **procedure
factory**. It creates a new, customized validation procedure
based on the values of its parameters. `make-validator`
takes two arguments: a procedure and an integer. It returns
*as its answer* a validation procedure that takes one
argument, a list of digits. This validator procedure determines
whether the list of digits form a valid number, according to its
values for `f` and `m`.

You can see for yourself that `make-validator` returns a
procedure by evaluating a call to it:

> (make-validator ISBN-f 11) #<procedure>

... and that the generated procedure works just like the one we defined above:

> ((make-validator ISBN-f 11) '(2 7 3 5 9 1 9 3 0 5)) #f > ((make-validator ISBN-f 11) '(2 7 3 5 9 1 9 2 1 9)) #f > ((make-validator ISBN-f 11) '(0 8 9 8 1 5 4 6 4 2)) #t

Now, we can define `valid-ISBN?` as a verifier that uses
the required values for `f` and `m`:

> (define valid-ISBN? (make-validator ISBN-f 11)) > (valid-ISBN? '(2 7 3 5 9 1 9 2 1 9)) #f > (valid-ISBN? '(0 8 9 8 1 5 4 6 4 2)) #t

We can also define `valid-UPC?` using `make-validator`:

> (define valid-UPC? (make-validator UPC-f 10)) > (valid-UPC? '(2 7 3 5 9 1 9 3 0 5)) #t > (valid-UPC? '(2 7 3 5 9 1 9 3 2 7)) #f > (valid-UPC? '(3 9 2 0 8 9 0 0 0 3)) #t

You will now be able to generate code for clients with similar problems quite easily! A productive programmer is a happy programmer :-) -- and she can also make her boss and clients happy!

Please download the self-verifying number code, and tinker with it as a way to understand how it -- and the idea of procedure as data -- works.