[Edu-sig] implementing summation notation (in Scheme & Python)

Kirby Urner pdx4d@teleport.com
Mon, 04 Jun 2001 09:44:59 -0700


The generic sigma notation concept is presented in Scheme 
by authors Abelson and Sussman [1] as follows [2]:

  (define (sum term a next b)
    (if (> a b)
        0
        (+ (term a)
           (sum term (next a) next b))))

a and b are start, end values, with (term a) giving the 
actual term to be summed -- which might be a itself -- 
and (next a) giving the next value for a.  In other words, 
both term and next are functions, not numbers.

Here's the same thing in Python:

   def sum(term, a, next, b):
	if a>b: return 0
	return term(a) + sum(term,next(a),next,b)

And a non-recursive version:

   def sum2(term, a, next, b):
	total = 0
	while a<b:
	   total += term(a)
	   a = next(a)
	return total

or we could do it this way:

   from operator import add

   def sum3(term,a,next,b):
	def indexlist(a,next,b):
  	   output = []
	   while not a>b:
	      output.append(a)
	      a = next(a)
	   return output
	return reduce(add,[term(x) for x in indexlist(a,next,b)])

Recursively:

   from __future__ import nested_scopes
   from operator import add

   def sum4(term,a,next,b):
       def indexlist(a,next,b):
	   if a>b: return []
     	   else: return [a] + indexlist(next(a),next,b)
       return reduce(add,[term(x) for x in indexlist(a,next,b)])

If working in IDLE, you need to save the above in a new window,
then do CTRL-F5 to add it into to your namespace.  This is 
because the from __future__ thing doesn't work in the shell
window.

There's another problem with sum4 though -- it doesn't take advantage
of locally scoped bindings, i.e. there's no need to pass next and
b as parameters to indexlist if these are fixed within the outer 
function.  So a rewrite of sum4 might be:

  def sum4(term,a,next,b):
      def indexlist(s=a):
	  if s>b: return []
    	  else: return [s] + indexlist(next(s))
      return reduce(add,[term(x) for x in indexlist(a)])

I've introduced s to show that it changes through iterations
and only initializes to a.  sum3 could similarly benefit from
local bindings:

  def sum3(term,a,next,b):
      def indexlist():
          s=a
    	   output = []
	   while not s>b:
	      output.append(s)
	      s = next(s)
	   return output
      return reduce(add,[term(x) for x in indexlist()])

although again, if in IDLE, this will involve transfering the
def to a saved module/window and executing it with CTRL-F5.

If you want to sum consecutive integers, term(a) is just a, 
so the authors write ident:

   (define (identity x) x)

and another function to increment by 1:

   (define (inc n) (+ n 1))

We'll do the same in Python and find 1+2+3+...+10:

   def ident(a): return a

   def inc(a): return a+1

   >>> sum(ident, 1, inc, 10)
   55
   >>> sum2(ident, 1, inc, 10)
   55
   >>> sum3(ident, 1, inc, 10)
   55
   >>> sum4(ident, 1, inc, 10)
   55

Scheme:

  (define (pi-sum a b)
    (define (pi-term x)
      (/ 1.0 (* x (+ x 2))))
    (define (pi-next x)
      (+ x 4))
    (sum pi-term a pi-next b))

  > (* 8 (pi-sum 1 1000))
  3.139592655589783

Python:

  def pi_sum(a,b):
     def pi_term(x): return 1.0/(x * (x+2))
     def pi_next(x): return x+4
     return sum(pi_term,a,pi_next,b)

  >>> 8 * pi_sum(1,1000)
  3.1395926555897828

-- the above summation converges to pi.

Kirby

[1] http://mitpress.mit.edu/sicp/full-text/book/book.html
[2] http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-12.html