[Edu-sig] foundations,

Kirby Urner pdx4d@teleport.com
Tue, 29 May 2001 08:37:38 -0700


I'm glad to know there's a module system of such sophistication.

Also, whereas the chapter I was citing introduces the rational 
number by means of numerous Scheme procedures, it wouldn't make
a lot of sense to go to all this trouble in PLT Scheme, where 
the fraction type is built in, e.g.:

 > (define a (/ 3 4))
 > a
 3/4
 > (define b (/ 1 12))
 > (+ a b )
 5/6
 > (- a b)
 2/3
 > (* a b)
 1/16
 > (/ a b)
 9

In Python, I'd do the same re modules:  store the Fraction class
in a module, then import.  I happen to have one handy:

  >>> from mathobjects import Fraction

modules bear some resemblance to classes in that both use the 
dictionary structure to define a table of contents.  You can get
at the keys to a class with dir:

  >>> dir(Fraction)
  ['__abs__', '__add__', '__div__', '__doc__', '__eq__', '__float__',
'__gt__', 
  '__init__', '__lt__', '__module__', '__mul__', '__neg__', '__pow__',
'__radd__', 
  '__rdiv__', '__repr__', '__rmul__', '__sub__', 'denom', 'fformat',
'float', 'list', 
  'mkfract', 'numer', 'recip', 'simplify']

Same with a module:

  >>> import mathobjects
  >>> dir(mathobjects)
  ['Fraction', 'Matrix', 'Poly', 'Sqmatrix', '__builtins__', '__doc__', 
   '__file__', '__name__', '__path__', 'deriv', 'polynomial', 'pyfraction',
   'simplematrix']

Then use the Fraction class to instantiate objects, a and b.  Because
the primitive operators have been overridden, your fraction objects 
behave as expected:

  >>> a = Fraction(3,4)
  >>> b = Fraction(1,12)
  >>> a+b
  (5/6.)
  >>> a-b
  (2/3.)
  >>> a*b
  (1/16.)
  >>> a/b
  9

It was my decision, in the __repr__ method, to have that little decimal
point in the denominator.  This allows conversion to float simply by
evaluating a stringified representation:

  >>> eval(str(a+b))
  0.83333333333333337

although __float__ is also defined:

  >>> float(a+b)
  0.83333333333333337

Using a parallel infrastructure, we would define polyhedra as another
kind of object, based on a list of coefficients as parameters:

  >>> from mathobjects import Poly
  >>> p = Poly([1,2,3,4])
  >>> p
  x**3 + 2*x**2 + 3*x + 4
  >>> q = Poly([3,-2,-1,-1])
  >>> p*q
  3*x**6 + 4*x**5 + 4*x**4 + 3*x**3 - 13*x**2 - 7*x - 4
  >>> p-q
  -2*x**3 + 4*x**2 + 4*x + 5
  >>> p+q
  4*x**3 + 2*x + 3

A fully generalized system would allow me to use polynomials
in the numerator and denominator of Fractions, but so far my
polynomial objects don't understand the / operator -- 
dividing polys is somewhat difficult, especially if you plan
to simplify them (which involves finding common factors, i.e.
the gcd).

However, I *am* able to use the Fraction objects as coefficients 
if I like:

  >>> r = Poly([a,b,1])
  >>> r
  (3/4.)*x**2 + (1/12.)*x + 1
  >>> r*p
  (3/4.)*x**5 + (19/12.)*x**4 + (41/12.)*x**3 + (21/4.)*x**2 + (10/3.)*x + 4

And to pass values to the polynomial, as a function:

  >>> s = r*p
  >>> s(10)
  (284437/3.)
  >>> s(-12)
  -158976

Note that s(10) returned a fraction, not a decimal.  This is because 
my evaluator processes coefficients as fractions, in order to make 
sure we don't loose any information as a result of casting to floating
point.

It was by building polynomials such as the above, and passing values
to them, that I developed the Bernoulli numbers:

  >>> bernoulli.bernseries(14)
  [1, (1/2.), (1/6.), 0, (-1/30.), 0, (1/42.), 0, (-1/30.), 0, (5/66.), 
  0, (-691/2730.), 0, (7/6.)]

The Matrix object, with subclass Sqmatrix (square matrix) works the 
same way, with *, ** (power), +, - and / overrridden.  The elements 
of a matrix may be Fractions or Polys.

So the idea is to learn about mathematical objects by building them,
just as we build radios or other models to learn about their design
and structure.  The result is, in this case, a primitive computer 
algebra system.

In math class, we would spend more time talking about fractions, 
lcm, gcd, the determinant of a matrix and so on, and less time 
worrying about the syntax and infrastructure of Python, which 
presumably they picked up somewhat earlier -- maybe in 8th grade,
whereas by this time we're in high school.

I gather you take a similar approach with Scheme, focusing 
increasingly on the knowledge domain as time goes on, with 
Scheme itself receiving less emphasis.

Kirby