About SICP The following Python code is derived from the examples provided in the book:
      "Structure and Interpretation of Computer Programs, Second Edition" by Harold Abelson and Gerald Jay Sussman with Julie Sussman.
      http://mitpress.mit.edu/sicp/

SICP Chapter #03 Examples in Python
import math

# Functions defined in previous chapters
def gcd(a, b):
   if (b == 0):
      return a
   else:
      return gcd(b, a % b)

def square(x): return x * x
def average(x, y):
   return (x + y) / 2.0
def has_no_divisors(n, c):
   if c == 1:
      return True
   elif n % c == 0:
      return False
   else:
      return has_no_divisors(n, c-1)
def isPrime(n):
   return has_no_divisors(n, n-1)
def enumerate_interval(low, high):
   rt = []
   for i in range(low, high+1):
      rt.append(i)
   return rt
def is_odd(n): return n % 2 == 1
def is_even(n): return not(is_odd(n))

# 3.1.1 - Assignment and State - State Variables
balance = 100

def withdraw(amount):
   global balance
   if balance >= amount:
      balance = balance - amount
      return balance
   else:
      print('InsufficientFunds: ' + str(balance))
      return ()

print (withdraw(25))
print (withdraw(25))
print (withdraw(60))
print (withdraw(15))

def new_withdraw():
   balance = [100]                                    # use list to overcome scoping intricacies in Python
   def withdraw(amount):
      if balance[0] >= amount:
         balance[0] = balance[0] - amount
         return balance[0]
      else:
         print('InsufficientFunds: ' + str(balance[0]))
         return ()
   return withdraw

def make_withdraw(init_balance):
   balance = [init_balance]
   def withdraw(amount):
      if balance[0] >= amount:
         balance[0] = balance[0] - amount
         return balance[0]
      else:
         print('InsufficientFunds: ' + str(balance[0]))
         return ()
   return withdraw

w1 = make_withdraw(100)
w2 = make_withdraw(100)

print (w1(50))
print (w2(70))
print (w2(40))
print (w1(40))

class Account:
   def __init__(self, init_balance):
      self.balance = init_balance
   def withdraw(self, amount):
      if self.balance >= amount:
         self.balance = self.balance - amount
         return self.balance
      else:
         print('InsufficientFunds: ' + str(self.balance))
         return ()
   def deposit(self, amount):
      self.balance = self.balance + amount
      return self.balance
   def getbalance(self):
      return self.balance

acc = Account(100)
acc.withdraw(50)
acc.withdraw(60)
acc.deposit(40)
acc.withdraw(60)
print (acc.getbalance())

Account(100)

# Exercise 3.1
# exercise left to reader to define appropriate functions
# a = make_accumulator(5)
# a.f(10)
# a.f(10)

# Exercise 3.2
# exercise left to reader to define appropriate functions
# s = make_monitored(math.sqrt)
# s.f(100)
# s.how_many_calls()

# Exercise 3.3
# exercise left to reader to define appropriate functions
# acc = Account(100, "secret-password")
# acc.withdraw(40, "secret-password")
# acc.withdraw(50, "some-other-password")

# 3.1.2 - Assignment and State - The Benefits of Introducing Assignment
random_init = 7

def rand_update(x):
   a = 27
   b = 26
   m = 127
   return (a*x + b) % m

def rand():
   global random_init
   x = random_init
   random_init = rand_update(random_init)
   return x

def cesaro_test():
   return gcd(rand(), rand()) == 1

def monte_carlo(trials, experiment):
   def iter (trials_remaining, trials_passed):
      if trials_remaining == 0:
         return trials_passed / trials
      elif experiment():
         return iter(trials_remaining - 1, trials_passed + 1)
      else:
         return iter(trials_remaining - 1, trials_passed)
   return iter(trials, 0)

def estimate_pi(trials):
   return math.sqrt(6.0 / monte_carlo(trials, cesaro_test))

print (estimate_pi(10))

# second version (no assignment)
def random_gcd_test(trials, initial_x):
   def iter(trials_remaining, trials_passed, x):
      x1 = rand_update(x)
      x2 = rand_update(x1)
      if trials_remaining == 0:
         return trials_passed / trials
      elif gcd(x1, x2) == 1:
         return iter(trials_remaining - 1, trials_passed + 1, x2)
      else:
         return iter(trials_remaining - 1, trials_passed, x2)
   return iter(trials, 0, initial_x)

def estimate_pi(trials):
   return math.sqrt(6.0 / random_gcd_test(trials, random_init))

random_init = 7
print (estimate_pi(10))

# Exercise 3.6
# exercise left to reader to define appropriate functions
# def random_in_range(low, high):
#    range = high - low
#    return low + random(range)

# 3.1.3 - Assignment and State - The Cost of Introducing Assignment
def make_simplified_withdraw(init_balance):
   balance = [init_balance]
   def withdraw(amount):
      balance[0] = balance[0] - amount
      return balance[0]
   return withdraw

w = make_simplified_withdraw(25)
print (w(20))
print (w(10))

def make_decrementer(balance):
   return lambda amount: balance - amount

d = make_decrementer(25)
print (d(20))
print (d(10))

make_decrementer(25)(20)
(lambda amount: 25 - amount)(20)
25 - 20

make_simplified_withdraw(25)(20)

# Sameness and change
d1 = make_decrementer(25)
d2 = make_decrementer(25)

w1 = make_simplified_withdraw(25)
w2 = make_simplified_withdraw(25)
print (w1(20))
print (w1(20))
print (w2(20))

peter_acc = Account(100)
paul_acc = Account(100)

peter_acc = Account(100)
paul_acc = peter_acc

# Pitfalls of imperative programming
def factorial(n):
   def iter(product, counter):
      if counter > n:
         return product
      else:
         return iter(counter * product, counter + 1)
   return iter(1, 1)

def factorial(n):
   product = 1
   counter = 1
   def iter():
      if counter > n:
         return product
      else:
         product = counter * product;
         counter = counter + 1;
         return iter()
   return iter()

# Exercise 3.7
# exercise left to reader to define appropriate functions
# paul_acc = make_joint(peter_acc, "open_sesame", "rosebud")

# 3.2.1 - The Environment Model of Evaluation - The Rules for Evaluation
def square(x): return x * x

square = lambda x: x * x

# 3.2.2 - The Environment Model of Evaluation - Applying Simple Procedures
def square(x): x * x

def sum_of_squares(x, y):
   return square(x) + square(y)

def f(a):
   return sum_of_squares(a + 1, a * 2)

# Exercise 3.9
def factorial(n):
   if n == 1:
      return 1
   else:
      return n * factorial(n - 1)

def fact_iter(product, counter, max_count):
   if counter > max_count:
      return product
   else:
      return fact_iter(counter * product, counter + 1, max_count)

def factorial(n):
   return fact_iter(1, 1, n)

# 3.2.3 - The Environment Model of Evaluation - Frames as Repository of State
def make_withdraw(init_balance):
   balance = [init_balance]
   def withdraw(amount):
      if balance[0] >= amount:
         balance[0] = balance[0] - amount
         return balance[0]
      else:
         print('InsufficientFunds: ' + str(balance[0]))
         return ()
   return withdraw

w1 = make_withdraw(100)
print (w1(50))
w2 = make_withdraw(100)

# Exercise 3.10
def make_withdraw(initial_amount):
   balance = [initial_amount]
   def withdraw(amount):
      if balance[0] >= amount:
         balance[0] = balance[0] - amount
         return balance[0]
      else:
         print('InsufficientFunds: ' + str(balance[0]))
         return nil
   return withdraw
w1 = make_withdraw(100)
print (w1(50))
w2 = make_withdraw(100)

# 3.2.4 - The Environment Model of Evaluation - Internal Definitions

# same as in section 1.1.8
def sqrt(x):
   def good_enough(guess):
      return abs(square(guess) - x) < 0.001
   def improve(guess):
      return average(guess, x / guess)
   def sqrt_iter(guess):
      if good_enough(guess):
         return guess
      else:
         return sqrt_iter(improve(guess))
   return sqrt_iter(1.0)

# Exercise 3.11
class Account:
   def __init__(self, init_balance):
      self.balance = init_balance
   def withdraw(self, amount):
      if self.balance >= amount:
         self.balance = self.balance - amount
         return self.balance
      else:
         print('InsufficientFunds: ' + str(self.balance))
         return ()
   def deposit(self, amount):
      self.balance = self.balance + amount
      return self.balance
   def getbalance(self):
      return self.balance

acc = Account(50)
acc.deposit(40)
acc.withdraw(60)
print (acc.getbalance())
acc2 = Account(100)

Chris Rathman / Chris.Rathman@tx.rr.com