About SICP The following Ruby 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 Ruby
# Functions defined in previous chapters
def gcd(a, b)
   if b == 0 then
      a
   else
      gcd(b, a % b)
   end
end

def square(x) x * x end
def average(x, y)
   (x + y) / 2.0
end
def has_no_divisors(n, c)
   if c == 1 then
      true
   elsif n % c == 0 then
      false
   else
      has_no_divisors(n, c-1)
   end
end
def isPrime(n)
   has_no_divisors(n, n-1)
end
def enumerate_interval(low, high)
   rt = []
   for i in low..high
      rt.push(i)
   end
   rt
end
def is_odd(n) n % 2 == 1 end
def is_even(n) not(is_odd(n)) end

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

def withdraw(amount)
   if $balance >= amount then
      $balance = $balance - amount
      $balance
   else
      puts('InsufficientFunds: ' + String($balance))
      return ()
   end
end

puts withdraw(25)
puts withdraw(25)
puts withdraw(60)
puts withdraw(15)

def new_withdraw()
   balance = 100
   withdraw = lambda{|amount|
      if balance >= amount then
         balance = balance - amount
         return balance
      else
         puts('InsufficientFunds: ' + String(balance))
         return ()
      end}
   withdraw
end

def make_withdraw(init_balance)
   balance = init_balance
   withdraw = lambda{|amount|
      if balance >= amount then
         balance = balance - amount
         return balance
      else
         puts('InsufficientFunds: ' + String(balance))
         return ()
      end}
   return withdraw
end

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

puts w1.call(50)
puts w2.call(70)
puts w2.call(40)
puts w1.call(40)

class Account
   attr :balance
   def initialize(init_balance)
      @balance = init_balance
   end
   def withdraw(amount)
      if @balance >= amount then
         @balance = @balance - amount
         @balance
      else
         puts('InsufficientFunds: ' + String(@balance))
         ()
      end
   end
   def deposit(amount)
      @balance = @balance + amount
      @balance
   end
   def getbalance()
      @balance
   end
end

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

Account.new(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
   (a*x + b) % m
end

def rand()
   x = $random_init
   $random_init = rand_update($random_init)
   x
end

def cesaro_test()
   gcd(rand(), rand()) == 1
end

def monte_carlo(trials, experiment)
   iter = lambda{|trials_remaining, trials_passed|
      if trials_remaining == 0 then
         Float(trials_passed) / trials
      elsif experiment.call()
         iter.call(trials_remaining - 1, trials_passed + 1)
      else
         iter.call(trials_remaining - 1, trials_passed)
      end}
   iter.call(trials, 0)
end

def estimate_pi(trials)
   Math.sqrt(6.0 / monte_carlo(trials, lambda{cesaro_test()}))
end

puts estimate_pi(10)

# second version (no assignment)
def random_gcd_test(trials, initial_x)
   iter = lambda{|trials_remaining, trials_passed, x|
      x1 = rand_update(x)
      x2 = rand_update(x1)
      if trials_remaining == 0 then
         Float(trials_passed) / trials
      elsif gcd(x1, x2) == 1 then
         iter.call(trials_remaining - 1, trials_passed + 1, x2)
      else
         iter.call(trials_remaining - 1, trials_passed, x2)
      end}
   iter.call(trials, 0, initial_x)
end

def estimate_pi(trials)
   Math.sqrt(6.0 / random_gcd_test(trials, $random_init))
end

$random_init = 7
puts estimate_pi(10)

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

# 3.1.3 - Assignment and State - The Cost of Introducing Assignment
def make_simplified_withdraw(init_balance)
   balance = init_balance
   withdraw = lambda{|amount|
      balance = balance - amount
      balance}
   withdraw
end

w = make_simplified_withdraw(25)
puts w.call(20)
puts w.call(10)

def make_decrementer(balance)
   lambda{|amount| balance - amount}
end

d = make_decrementer(25)
puts d.call(20)
puts d.call(10)

make_decrementer(25).call(20)
(lambda{|amount| 25 - amount}).call(20)
25 - 20

make_simplified_withdraw(25).call(20)

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

w1 = make_simplified_withdraw(25)
w2 = make_simplified_withdraw(25)
puts w1.call(20)
puts w1.call(20)
puts w2.call(20)

peter_acc = Account.new(100)
paul_acc = Account.new(100)

peter_acc = Account.new(100)
paul_acc = peter_acc

# Pitfalls of imperative programming
def factorial(n)
   iter = lambda{|product, counter|
      if counter > n then
         product
      else
         iter.call(counter * product, counter + 1)
      end}
   iter.call(1, 1)
end

def factorial(n)
   product = 1
   counter = 1
   iter = lambda{
      if counter > n then
         product
      else
         product = counter * product;
         counter = counter + 1;
         iter.call()
      end}
   iter.call()
end

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

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

square = lambda{|x| x * x}

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

def sum_of_squares(x, y)
   square(x) + square(y)
end

def f(a)
   sum_of_squares(a + 1, a * 2)
end

# Exercise 3.9
def factorial(n)
   if n == 1 then
      1
   else
      n * factorial(n - 1)
   end
end

def fact_iter(product, counter, max_count)
   if counter > max_count then
      product
   else
      fact_iter(counter * product, counter + 1, max_count)
   end
end

def factorial(n)
   fact_iter(1, 1, n)
end

# 3.2.3 - The Environment Model of Evaluation - Frames as Repository of State
def make_withdraw(init_balance)
   balance = init_balance
   withdraw = lambda{|amount|
      if balance >= amount then
         balance = balance - amount
         balance
      else
         puts('InsufficientFunds: ' + String(balance))
         ()
      end}
   withdraw
end

w1 = make_withdraw(100)
puts w1.call(50)
w2 = make_withdraw(100)

# Exercise 3.10
def make_withdraw(initial_amount)
   balance = initial_amount
   withdraw = lambda{|amount|
      if balance >= amount then
         balance = balance - amount
         balance
      else
         puts('InsufficientFunds: ' + String(balance))
         return ()
      end}
   withdraw
end
w1 = make_withdraw(100)
puts w1.call(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)
   good_enough = lambda{|guess|
      abs(square(guess) - x) < 0.001
   }
   improve = lambda{|guess|
      average(guess, Float(x) / guess)
   }
   sqrt_iter = lambda{|guess|
      if good_enough.call(guess) then
         guess
      else
         sqrt_iter.call(improve.call(guess))
      end
   }
   sqrt_iter.call(1.0)
end

# Exercise 3.11
class Account
   attr :balance
   def initialize(init_balance)
      @balance = init_balance
   end
   def withdraw(amount)
      if @balance >= amount:
         @balance = @balance - amount
         @balance
      else
         puts('InsufficientFunds: ' + String(@balance))
         ()
      end
   end
   def deposit(amount)
      @balance = @balance + amount
      @balance
   end
   def getbalance()
      @balance
   end
end

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

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