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