Problem: Given the magnitude of an angle, expressed in radians, compute the angle's sine.
Ideally, we should be able to figure the sine of x as the limit of
the infinite series x/1! - x^3/3! + x^5/5! -
x^7/7! + ... . It is convenient to represent a series in Scheme as
a procedure that takes any positive integer k as an argument and
returns the value of the kth term. If we had a procedure
limit-of-series that took take any convergent series as its
argument and return the limit of that series, we could simply write
(define sine
(lambda (x)
(let ((sine-series
(lambda (n)
(let ((index (+ n n -1)))
((if (odd? n) + -) (/ (expt x index) (factorial index)))))))
(limit-of-series sine-series))))
as a direct translation of the mathematical definition. Unfortunately, computing the limit of a series arithmetically requires an infinite amount of time. In many cases, however, one can halt either when a specified number of terms has been computed or when sucessive partial sums differ by a sufficiently small amount. In either case, the limit will be computed only approximately; but in general the arguments to the sine function will be inexact numbers anyway.
Here is a version of limit-of-series that computes the value
of the series at the twelfth term:
(define limit-of-series
(let ((number-of-terms 12))
(lambda (series)
(let loop ((total 0)
(k number-of-terms))
(if (zero? k)
total
(loop (+ total (series k)) (- k 1)))))))
In this case the computation has been arranged so that the terms that are
likely to have the least absolute values are summed first, since this will
postpone the loss of precision.
Here is a version of limit-of-series that continues the series
until successive partial sums differ by less than 0.000001:
(define limit-of-series
(let ((epsilon 1e-6))
(lambda (series)
(let loop ((total (series 1))
(previous-total 0)
(k 2))
(if (< (abs (- total previous-total)) epsilon)
total
(loop (+ total (series k)) total (+ k 1)))))))
Either approach gives a sine function that yields plausible values for
arguments close to zero, rapidly deteriorating in accuracy once |x|
exceeds 1. (The difficulty when |x| > 1 is that the computation
involves adding inexact values of large, nearly equal magnitude and
opposite sign, so that many or all of the significant digits are cancelled
out.)
This document is available on the World Wide Web as
http://www.math.grin.edu/~stone/events/scheme-workshop/sines.html