CSC 270 - Survey of Programming Languages
Scheme (Racket) Lecture 9: Natural Numbers
Defining Natural Numbers
- People normally introduce natural numbers via enumeration:
0, 1, 2, etc
- For us, the ``etc.'' does not suffice, especially if we wish to develop
programs that consume natural numbers.
-
The only way to remove the informal ``etc.'' from the enumeration is to
describe the collection of numbers with a self-referential description.
Here is a first attempt:
-
0 is a natural number.
-
If n is a natural number, then one more than n is one, too.
-
Rewriting this in Scheme:
-
A natural number (N) is either
-
0
or
-
(add1 n)
if n is a natural
number.
-
The operation add1 adds 1 to a natural number. Of course, we could use
(+ ... 1) but add1 stands out and signals "natural number", as opposed
to arbitrary number, to the reader of a data definition and a program.
-
It is instructive to construct examples from the data definition. Clearly,
-
0 is the first natural number, so
-
(add1 0) is the next one.
-
From here, it is easy to see the pattern:
-
(
add1 (add1 0))
-
(add1 (add1 (add1 0)))
-
(add1 (add1 (add1 (add1 0))))
- The examples also show that natural numbers are constructed like
lists.
- We start with a basic one, 0, and construct larger ones with add1.
Processing Natural Numbers of Arbitrary Size
- Let us develop the program hellos. The program consumes a natural number
n and produces a list of n copies of 'hello. The contract for this program is
easy to formulate:
;; hellos : N -> list-of-symbols
;; to create a list of n copies of
'hello
(define (hellos n) ...)
- And making up program examples is also straightforward:
(hellos 0) = empty
-
(hellos 2) = (cons 'hello (cons
'hello empty))
The design of a
template for hellos follows the design recipe for self-referential data
definitions. We immediately see that hellos is a conditional program, that
its cond-expression has two clauses, and that the first clause must distinguish
0 from other possible inputs:
(define (hellos n)
(cond
[(= n 0) ...]
[else ...]))
- Furthermore, the data definition says that 0 is an atomic value and every
other natural number is a compound datum and "contains" the predecessor to
which 1 was added.
- So, if n is not 0, we must subtract
1 from n. Since the result is also a natural number, so according to the
design recipe we wrap the expression with (hellos ...):
(define (hellos n)
(cond
[(zero? n) ...]
[else ... (hellos
(sub1 n)) ... ]))
- Now we are ready to proceed with the definition.
- Assume (zero? n) evaluates to true. Then the answer must be empty, as
the examples illustrate.
( define
(hellos n)
(cond
[(zero? n) empty]
[else ... ]))
- Assume the input is greater than 0 (e. g., let's say it is 2).
- According to the suggestion in the template, hellos should use (hellos 1)
to compute a part of the answer.
- In general, (hellos (sub1 n)) produces a list that contains n-1
occurrences of 'hello. Clearly,to produce a list with n occurrences, we
simply cons another 'hello onto this list:
(define (hellos n)
(cond
[(zero? n) empty]
[else (cons 'hello
(hellos (sub1 n)))]))
- Assume the input is greater than 0 (e. g., let's say it is 2).
- According to the suggestion in the template, hellos should use (hellos 1)
to compute a part of the answer.
- In general,
(hellos (sub1 n))
produces a list
that contains n-1 occurrences of 'hello. Clearly,to produce a list with
n occurrences, we simply cons another 'hello onto this list:
(define (hellos n)
(cond
[(zero? n) empty]
[else (cons 'hello
(hellos (sub1 n)))]))
- Let us test hellos with some hand-evaluations:
(hellos 0)
= (cond [(zero? 0) empty]
[else (cons 'hello
(hellos (sub1 0)))])
= (cond [true empty]
[else (cons 'hello
(hellos (sub1 0)))])
= empty
-
(hellos 2)
= (cond
[(zero? 2) empty]
[else (cons 'hello
(hellos (sub1 2)))])
= (cond
[false empty]
[else (cons 'hello
(hellos (sub1 2)))])
= (cons 'hello (hellos (sub1 2)))
= (cons 'hello )
= (cons 'hello (cons 'hello empty))
Alternative Formulations
-
Let's take a look at the factorial function:
n! = n ·(n-1) ·(n-2)
· (n-3) · 3 · 2· 1
- We can define it:
- Multiply together every counting number less than or equal to n
- or
0! = 1
n! = n ·(n-1)! where n > 0
Alternative Formulations - Factorial
- The program is called factorial and has the mathematical notation !. Its
contract is easy to formulate:
;; ! : N -> N
;; to compute
(define (! n) ...)
- It consumes a natural number and produces one.
- Specifying its input-output relationship
is a bit more tricky. We know, what 1·2·3 is, so we should have
(! 3)
= 6
and (!
10)
=
3628800
- What to do with the input 0? We solve the follow mathematical convention
and set that
(! 0)
= 1.
- The rest is straightforward. The template for ! is that of a natural
number-processing program:
(define (! n)
(cond
[(zero? n) ... ]
[else ... (!
(sub1 n))... ]))
-
If n = 0, the result must be 1:
(define (! n)
(cond
[(zero? n) 1 ]
[else ... (!
(sub1 n))... ]))
-
For the other cases, it will be n ·
(n-1)!, which is:
(define (! n)
(cond
[(zero? n) 1 ]
[else (*
n (! (sub1 n)))]
)
)
[Back to the Notes Index]