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)
|