Skip to content

Instantly share code, notes, and snippets.

@melborne
Created February 15, 2009 08:33
Show Gist options
  • Save melborne/64646 to your computer and use it in GitHub Desktop.
Save melborne/64646 to your computer and use it in GitHub Desktop.
SICP with Ruby
require "schemed"
def deriv(exp, var)
case exp
when Numeric
0
when Symbol, String
same_variable?(exp, var) ? 1 : 0
when Sum
make_sum deriv(addend(exp), var), deriv(augend(exp), var)
when Product
m1 = make_product multiplier(exp), deriv(multiplicand(exp), var)
m2 = make_product deriv(multiplier(exp), var), multiplicand(exp)
make_sum(m1, m2)
when Exponentiation
e1 = make_product(exponent(exp), make_exponentiation(base(exp), exponent(exp)-1))
e2 = deriv(base(exp), var)
make_product(e1, e2)
else
raise "unknown expression type -- DERIV #{exp.inspect}"
end
end
def same_variable?(exp, var)
exp == var ? true : false
end
class Sum
def self.===(x)
pair?(x) and car(x).equal? :+
end
end
class Product
def self.===(x)
pair?(x) and car(x).equal? :*
end
end
class Exponentiation
def self.===(x)
pair?(x) and car(x).equal? :**
end
end
def make_sum(a1, a2)
if eql_number?(a1, 0)
a2
elsif eql_number?(a2, 0)
a1
elsif Numeric === a1 and Numeric === a2
a1 + a2
else
list :+, a1, a2
end
end
def eql_number?(exp, num)
Numeric === exp and exp == num
end
def make_product(m1, m2)
if eql_number?(m1, 0) or eql_number?(m2, 0)
0
elsif m1 == 1
m2
elsif m2 == 1
m1
elsif Numeric === m1 and Numeric === m2
m1 * m2
else
list :*, m1, m2
end
end
def addend(s)
cadr s
end
def augend(s)
caddr s
end
def multiplier(p)
cadr p
end
def multiplicand(p)
caddr p
end
def base(e)
cadr e
end
def exponent(e)
caddr e
end
def make_exponentiation(e1, e2)
if e2 == 0
1
elsif e2 == 1
e1
else
list :**, e1, e2
end
end
list_p deriv list(:+, :x, 3), :x
list_p deriv list(:*, :x, :y), :x
list_p deriv list(:*, list(:*, :x, :y), list(:+, :x, 3)), :x
list_p deriv list(:+, list(:*, :a, list(:**, :x, 2)), list(:*, :b, :x)), :x
(define (p x) (display x)(newline))
(define (deriv exp var)
(cond ((number? exp) 0)
((variable? exp)
(if (same_variable? exp var) 1 0))
((sum? exp)
(make_sum (deriv (addend exp) var)
(deriv (augend exp) var)))
((product? exp)
(make_sum
(make_product (multiplier exp)
(deriv (multiplicand exp) var))
(make_product (deriv (multiplier exp) var)
(multiplicand exp))))
((exponentiation? exp)
(make_product
(make_product (exponent exp)
(make_exponentiation (base exp)
(- (exponent exp) 1)))
(deriv (base exp) var)))
(else
(error "unknown expression type -- DERIV" exp))))
(define (variable? x) (symbol? x))
(define (same_variable? v1 v2)
(and (variable? v1) (variable? v2) (eq? v1 v2)))
(define (make_sum a1 a2)
(cond ((=number? a1 0) a2)
((=number? a2 0) a1)
((and (number? a1) (number? a2)) (+ a1 a2))
(else (list `+ a1 a2))))
(define (=number? exp num)
(and (number? exp) (= exp num)))
(define (make_product m1 m2)
(cond ((or (=number? m1 0) (=number? m2 0)) 0)
((=number? m1 1) m2)
((=number? m2 1) m1)
((and (number? m1) (number? m2)) (* m1 m2))
(else (list `* m1 m2))))
(define (sum? x)
(and (pair? x) (eq? (car x) `+)))
(define (addend s) (cadr s))
(define (augend s) (caddr s))
(define (product? x)
(and (pair? x) (eq? (car x) `*)))
(define (multiplier p) (cadr p))
(define (multiplicand p) (caddr p))
(define (exponentiation? x)
(and (pair? x) (eq? (car x) `**)))
(define (base e) (cadr e))
(define (exponent e) (caddr e))
(define (make_exponentiation e1 e2)
(cond ((=number? e2 0) 1)
((=number? e2 1) e1)
(else (list `** e1 e2))))
(p (deriv `(+ x 3) `x))
(p (deriv `(* x y) `x))
(p (deriv `(* (* x y) (+ x 3)) `x))
(p (deriv `(+ (* a (** x 2)) (* b x)) `x))
require "transform"
def paint_wave(frame)
wave_shape(frame).call(frame)
end
def paint_wave4(frame)
flipped_pairs(wave_shape(frame)).call(frame)
end
def paint_diamond8(frame)
half = flipped_pairs(diamond_shape(frame))
below(half, half).call(frame)
end
def paint_wave_recur(frame, n=3)
square_limit(wave_shape(frame), n).call(frame)
end
def paint_diamond_recur(frame, n=3)
square_limit(diamond_shape(frame), n).call(frame)
end
def paint_frame(frame)
frame_shape(frame).call(frame)
end
def paint_wave4_with_frame(frame)
paint_wave4(frame)
paint_frame(frame)
end
def paint_x(frame)
x_shape(frame).call(frame)
end
def paint_star(frame)
star_shape(frame).call(frame)
end
def paint_star_recur(frame, n=3)
square_limit(star_shape(frame), n).call(frame)
end
if $0 == __FILE__
def draw_line(s1, s2)
p s1 + s2
end
frame = make_frame(make_vect(0, 0), make_vect(0, 1), make_vect(1, 0))
paint_wave4(frame)
end
(define (frame_coord_map frame)
(lambda (v)
(add_vect
(origin_frame frame)
(add_vect (scale_vect (xcor_vect v)
(edge1_frame frame))
(scale_vect (ycor_vect v)
(edge2_frame frame))))))
(define (segments->painter segment_list)
(lambda (frame)
(for-each
(lambda (segment)
(draw_line
((frame_coord_map frame) (start_segment segment))
((frame_coord_map frame) (end_segment segment))))
segment_list)))
(define (make_vect x y)
(cons x y))
(define (xcor_vect v)
(car v))
(define (ycor_vect v)
(cdr v))
(define (add_vect v1 v2)
(make_vect (+ (xcor_vect v1)
(xcor_vect v2))
(+ (ycor_vect v1)
(ycor_vect v2))))
(define (sub_vect v1 v2)
(make_vect (- (xcor_vect v1)
(xcor_vect v2))
(- (ycor_vect v1)
(ycor_vect v2))))
(define (scale_vect s v)
(make_vect (* s (xcor_vect v))
(* s (ycor_vect v))))
(define (make_frame origin edge1 edge2)
(list origin edge1 edge2))
(define (origin_frame frame)
(car frame))
(define (edge1_frame frame)
(cadr frame))
(define (edge2_frame frame)
(cadr (cdr frame)))
(define (make_segment x1 y1 x2 y2)
(cons (make_vect x1 y1) (make_vect x2 y2)))
(define (start_segment segment)
(car segment))
(define (end_segment segment)
(cdr segment))
(define normal_frame (make_frame (make_vect 0 0) (make_vect 1 0) (make_vect 0 1)))
(define (draw_line s1 s2)
(display s1)
(display s2)
(newline))
(define (frame_painter frame)
((segments->painter
(list (make_segment 0 0 0 1)
(make_segment 0 1 1 1)
(make_segment 1 1 1 0)
(make_segment 1 0 0 0)))
frame))
(define (x_painter frame)
((segments->painter
(list (make_segment 0 0 1 1)
(make_segment 0 1 1 0)))
frame))
(define (diamond_painter frame)
((segments->painter
(list (make_segment 0 0.5 0.5 1)
(make_segment 0.5 1 1 0.5)
(make_segment 1 0.5 0.5 0)
(make_segment 0.5 0 0 0.5)))
frame))
(define (wave frame)
((segments->painter
(list (make_segment 0.3 0 0.4 0.5)
(make_segment 0.4 0.5 0 0.6)
(make_segment 0 0.8 0.2 0.7)
(make_segment 0.2 0.7 0.4 0.7)
(make_segment 0.4 0.7 0.3 0.8)
(make_segment 0.3 0.8 0.4 1)
(make_segment 0.6 1 0.7 0.8)
(make_segment 0.7 0.8 0.6 0.7)
(make_segment 0.6 0.7 0.8 0.7)
(make_segment 0.8 0.7 1 0.4)
(make_segment 1 0.3 0.6 0.5)
(make_segment 0.6 0.5 0.7 0)
(make_segment 0.6 0 0.5 0.2)
(make_segment 0.5 0.2 0.4 0)))
frame))
require "painter"
require "transform"
Shoes.app :width => 400, :height => 420 do
@h = { 'Wave' => :paint_wave,
'Wave 4' => :paint_wave4,
'Wave Recursive' => :paint_wave_recur,
'Diamond 8' => :paint_diamond8,
'Diamond Recursive' => :paint_diamond_recur,
'Star' => :paint_star,
'Star Recursive' => :paint_star_recur,
'X' => :paint_x,
'Frame' => :paint_frame,
'Wave 4 in Frame' => :paint_wave4_with_frame }
frame = make_frame(make_vect(width/2-150, height/2+120), make_vect(300, 0), make_vect(0, -300))
@slots = []
background firebrick
flow do
list_box :items => ["Wave", "Wave 4", "Wave Recursive", "Diamond 8", "Diamond Recursive", "Star", "Star Recursive", "X", "Frame", "Wave 4 in Frame"],
:width => 200,
:choose => "wave" do |list|
@slots << stack { send(@h[list.text], frame) }
end
button "clear" do @slots.each { |s| s.clear} end
end
end
def cons(a, b=nil)
[a, b]
end
def car(items)
case items
when Array
items[0]
else
raise "bad argument type"
end
end
def cdr(items)
case items
when Array
items[1]
else
raise "bad argument type"
end
end
def cadr(items)
case items
when Array
items[1][0]
else
raise "bad argument type"
end
end
def caddr(items)
case items
when Array
items[1][1][0]
else
raise "bad argument type"
end
end
def list(*i)
if i.empty?
nil
else
cons i.shift, list(*i)
end
end
def list_ref(items, n)
if n == 0
car items
else
list_ref(cdr(items), n-1)
end
end
def length(items)
if items.nil?
0
else
1 + length(cdr items)
end
end
def append(list1, list2)
if list1.nil?
list2
else
cons car(list1), append(cdr(list1), list2)
end
end
def map(proc, items)
if items.nil?
nil
else
cons proc.call(car items), map(proc, cdr(items))
end
end
def scale_list(items)
map(lambda { |x| x**2 }, items)
end
def pair?(items)
case items
when Array
true
else
false
end
end
def count_leaves(x)
case
when x.nil? then 0
when !pair?(x) then 1
else
count_leaves(car x) + count_leaves(cdr x)
end
end
def scale_tree(tree, factor)
case
when tree.nil? then nil
when !pair?(tree) then tree * factor
else
cons scale_tree(car(tree), factor), scale_tree(cdr(tree), factor)
end
end
l = list(1, (list 2, (list 3, 4), 5), (list 6, 7))
scale_tree l, 10
class List < Array
def list_ref(n)
self[n]
end
def append(list)
self + list
end
def last_pair
self[-1]
end
def scale_list(n)
self.inject([]) { |arr, e| arr << e * n }
end
def map
self.inject([]) { |arr, e| arr << yield(e) }
end
def count_leaves
self.inject(0) do |len, e|
case e
when List
len + e.count_leaves
else
len + 1
end
end
end
def map_tree
self.inject([]) do |arr, e|
case e
when List
arr << e.map_tree{ |x| yield(x) }
else
arr << yield(e)
end
end
end
end
def list_p(list)
case list
when Array
puts _p(list).to_s.gsub(/\[/, "(").gsub(/\]/, ")").gsub(/[\:\"\,]/, "")
else
puts list
end
end
def _p(list)
_car = car list
_cdr = cdr list
case _car
when Array
if _cdr.nil?
[_p(_car)]
else
[_p(_car)] + _p(_cdr)
end
else
if _cdr.nil?
[_car]
else
(_p(_cdr)).unshift _car
end
end
end
def accumulate(op, initial, sequence)
if sequence.nil?
initial
else
send(op, car(sequence), (accumulate(op, initial, cdr(sequence))))
end
end
def prime?(n)
2.upto(n-1) do |i|
return false if n.modulo(i).zero?
end
true
end
def enumerate_interval(low, high)
if low > high
nil
else
cons low, enumerate_interval(low+1, high)
end
end
def filter(predicate, sequence)
case
when sequence.nil? then nil
when predicate.call(car sequence)
cons car(sequence), filter(predicate, cdr(sequence))
else
filter(predicate, cdr(sequence))
end
end
def flatmap(proc, seq)
accumulate(:append, nil, map(proc, seq))
end
def permutations(s)
mapping = lambda { |x| map(lambda { |p| cons x, p }, permutations(remove x, s)) }
if s.nil?
list nil
else
flatmap(mapping, s)
end
end
def remove(item, seq)
filter(lambda { |x| x != item }, seq)
end
(define (enumerate_interval low high)
(if (> low high) `()
(cons low (enumerate_interval (+ low 1) high))))
(define (accumulate op initial sequence)
(if (null? sequence)
initial
(op (car sequence)
(accumulate op initial (cdr sequence)))))
(define (filter predicate sequence)
(cond ((null? sequence) `())
((predicate (car sequence))
(cons (car sequence)
(filter predicate (cdr sequence))))
(else (filter predicate (cdr sequence)))))
(define (smallest_divisor n)
(find_divisor n 2))
(define (find_divisor n test_divisor)
(cond ((> (square test_divisor) n) n)
((divides? test_divisor n) test_divisor)
(else (find_divisor n (+ test_divisor 1)))))
(define (divides? a b)
(= (remainder b a) 0))
(define (prime? n)
(= n (smallest_divisor n)))
(define (square x)
(* x x))
(define (map proc items)
(if (null? items) `()
(cons (proc (car items)) (map proc (cdr items)))))
(define (flatmap proc seq)
(accumulate append `() (map proc seq)))
(define (p x) (display x) (newline))
(define (permutations s)
(if (null? s)
    (list `())
 (flatmap (lambda (x)
(map (lambda (p) (cons x p))
(permutations (remove x s))))
s)))
(define (remove item seq)
(filter (lambda (x) (not (= x item)))
seq))
require "schemed"
def frame_coord_map(frame)
lambda do |v|
scaled_edge1 = scale_vect(xcor_vect(v), edge1_frame(frame))
scaled_edge2 = scale_vect(ycor_vect(v), edge2_frame(frame))
add_vect(origin_frame(frame), add_vect(scaled_edge1, scaled_edge2))
end
end
def make_vect(x, y)
cons x, y
end
def xcor_vect(v)
car v
end
def ycor_vect(v)
cdr v
end
def add_vect(v1, v2)
make_vect(xcor_vect(v1)+xcor_vect(v2),
ycor_vect(v1)+ycor_vect(v2))
end
def sub_vect(v1, v2)
make_vect(xcor_vect(v1)-xcor_vect(v2),
ycor_vect(v1)-ycor_vect(v2))
end
def scale_vect(s, v)
make_vect(s*xcor_vect(v), s*ycor_vect(v))
end
def make_frame(origin, edge1, edge2)
list origin, edge1, edge2
end
def origin_frame(frame)
car frame
end
def edge1_frame(frame)
cadr frame
end
def edge2_frame(frame)
cadr(cdr frame)
end
def segments_to_painter(segment_list)
lambda do |frame|
segment_list.each do |segment|
x1, y1 = frame_coord_map(frame).call(start_segment(segment))
x2, y2 = frame_coord_map(frame).call(end_segment(segment))
line x1, y1, x2, y2
end
end
end
def make_segment(x1, y1, x2, y2)
cons make_vect(x1, y1), make_vect(x2, y2)
end
def start_segment(segment)
car segment
end
def end_segment(segment)
cdr segment
end
def draw_line(s1, s2)
p s1 + s2
end
#shapes
def frame_shape(frame)
segments_to_painter(List[make_segment(0.0,0.0,0.0,1.0),
make_segment(0.0,1.0,1.0,1.0),
make_segment(1.0,1.0,1.0,0.0),
make_segment(1.0,0.0,0.0,0.0)])
end
def x_shape(frame)
segments_to_painter(List[make_segment(0.0, 0.0, 1.0, 1.0),
make_segment(0.0, 1.0, 1.0, 0.0)])
end
def diamond_shape(frame)
segments_to_painter(List[make_segment(0.0, 0.5, 0.5, 1.0),
make_segment(0.5, 1.0, 1.0, 0.5),
make_segment(1.0, 0.5, 0.5, 0.0),
make_segment(0.5, 0.0, 0.0, 0.5)])
end
def wave_shape(frame)
segments_to_painter(List[make_segment(0.3, 0.0, 0.4, 0.5),
make_segment(0.4, 0.5, 0.0, 0.6),
make_segment(0.0, 0.8, 0.2, 0.7),
make_segment(0.2, 0.7, 0.4, 0.7),
make_segment(0.4, 0.7, 0.3, 0.8),
make_segment(0.3, 0.8, 0.4, 1.0),
make_segment(0.6, 1.0, 0.7, 0.8),
make_segment(0.7, 0.8, 0.6, 0.7),
make_segment(0.6, 0.7, 0.8, 0.7),
make_segment(0.8, 0.7, 1.0, 0.4),
make_segment(1.0, 0.3, 0.6, 0.5),
make_segment(0.6, 0.5, 0.7, 0.0),
make_segment(0.6, 0.0, 0.5, 0.2),
make_segment(0.5, 0.2, 0.4, 0.0)])
end
def star_shape(frame)
segments_to_painter(List[make_segment(0.0, 0.25, 0.5, 1.0),
make_segment(0.5, 1.0, 1.0, 0.25),
make_segment(1.0, 0.25, 0.0, 0.25),
make_segment(0.0, 0.75, 1.0, 0.75),
make_segment(1.0, 0.75, 0.5, 0.0),
make_segment(0.5, 0.0, 0.0, 0.75)])
end
require "shape"
def transform_painter(painter, origin, corner1, corner2)
lambda do |frame|
m = frame_coord_map(frame)
new_origin = m.call(origin)
painter.call(make_frame(new_origin,
sub_vect(m.call(corner1), new_origin),
sub_vect(m.call(corner2), new_origin)))
end
end
def flip_vert(painter)
transform_painter(painter, make_vect(0.0, 1.0),
make_vect(1.0, 1.0),
make_vect(0.0, 0.0))
end
def flip_horiz(painter)
transform_painter(painter, make_vect(1.0, 0.0),
make_vect(0.0, 0.0),
make_vect(1.0, 1.0))
end
def shrink_to_upper_right(painter)
transform_painter(painter, make_vect(0.5, 0.5),
make_vect(1.0, 0.5),
make_vect(0.5, 1.0))
end
def ratate90(painter)
transform_painter(painter, make_vect(1.0, 0.0),
make_vect(1.0, 1.0),
make_vect(0.0, 0.0))
end
def ratate180(painter)
transform_painter(painter, make_vect(1.0, 1.0),
make_vect(0.0, 1.0),
make_vect(1.0, 0.0))
end
def ratate270(painter)
transform_painter(painter, make_vect(0.0, 1.0),
make_vect(0.0, 0.0),
make_vect(1.0, 1.0))
end
def suqash_inwards(painter)
transform_painter(painter, make_vect(0.0, 0.0),
make_vect(0.65, 0.35),
make_vect(0.35, 0.65))
end
def beside(painter1, painter2)
split_point = make_vect(0.5, 0.0)
paint_left = transform_painter(painter1, make_vect(0.0, 0.0),
split_point,
make_vect(0.0, 1.0))
paint_right = transform_painter(painter2, split_point,
make_vect(1.0, 0.0),
make_vect(0.5, 1.0))
lambda do |frame|
paint_left.call(frame)
paint_right.call(frame)
end
end
def below(painter1, painter2)
split_point = make_vect(0.0, 0.5)
paint_bottom = transform_painter(painter1, make_vect(0.0, 0.0),
make_vect(1.0, 0.0),
split_point)
paint_top = transform_painter(painter2, split_point,
make_vect(1.0, 0.5),
make_vect(0.0, 1.0))
lambda do |frame|
paint_bottom.call(frame)
paint_top.call(frame)
end
end
def flipped_pairs(painter)
painter2 = beside(painter, flip_vert(painter))
below(painter2, painter2)
end
def right_split(painter, n)
if n == 0
painter
else
smaller = right_split(painter, n-1)
beside(painter, below(smaller, smaller))
end
end
def up_split(painter, n)
if n == 0
painter
else
smaller = up_split(painter, n-1)
below painter, beside(smaller, smaller)
end
end
def corner_split(painter, n)
if n == 0
painter
else
up = up_split(painter, n-1)
right = right_split(painter, n-1)
top_left = beside up, up
bottom_right = below right, right
corner = corner_split(painter, n-1)
beside below(painter, top_left), below(bottom_right, corner)
end
end
def square_limit(painter, n)
quarter = corner_split(painter, n)
half = beside(flip_horiz(quarter), quarter)
below flip_vert(half), half
end
(load "painter.scm")
(define (transform_painter painter origin corner1 corner2)
(lambda (frame)
(let ((m (frame_coord_map frame)))
(let ((new_origin (m origin)))
(painter
(make_frame new_origin
(sub_vect (m corner1) new_origin)
(sub_vect (m corner2) new_origin)))))))
(define (flip_vert painter)
(transform_painter painter
(make_vect 0.0 1.0)
(make_vect 1.0 1.0)
(make_vect 0.0 0.0)))
(define (flip_horiz painter)
(transform_painter painter
(make_vect 1.0 0.0)
(make_vect 0.0 0.0)
(make_vect 1.0 1.0)))
(define (shrink_to_upper_right painter)
(transform_painter painter
(make_vect 0.5 0.5)
(make_vect 1.0 0.5)
(make_vect 0.5 1.0)))
(define (ratate90 painter)
(transform_painter painter
(make_vect 1.0 0.0)
(make_vect 1.0 1.0)
(make_vect 0.0 0.0)))
(define (ratate180 painter)
(transform_painter painter
(make_vect 1.0 1.0)
(make_vect 0.0 1.0)
(make_vect 1.0 0.0)))
(define (ratate270 painter)
(transform_painter painter
(make_vect 0.0 1.0)
(make_vect 0.0 0.0)
(make_vect 1.0 1.0)))
(define (suqash_inwards painter)
(transform_painter painter
(make_vect 0.0 0.0)
(make_vect 0.65 0.35)
(make_vect 0.35 0.65)))
(define (beside painter1 painter2)
(let ((split_point (make_vect 0.5 0.0)))
(let ((paint_left
(transform_painter painter1
(make_vect 0.0 0.0)
split_point
(make_vect 0.0 1.0)))
(paint_right
(transform_painter painter2
split_point
(make_vect 1.0 0.0)
(make_vect 0.5 1.0))))
(lambda (frame)
(paint_left frame)
(paint_right frame)))))
(define (below painter1 painter2)
(let ((split_point (make_vect 0.0 0.5)))
(let ((paint_bottom
(transform_painter painter1
(make_vect 0.0 0.0)
(make_vect 1.0 0.0)
split_point))
(paint_top
(transform_painter painter2
split_point
(make_vect 1.0 0.5)
(make_vect 0.0 1.0))))
(lambda (frame)
(paint_bottom frame)
(paint_top frame)))))
(define (flipped_pairs painter)
(let ((painter2 (beside painter (flip_vert painter))))
(below painter2 painter2)))
(define (right_split painter n)
(if (= n 0)
painter
(let ((smaller (right_split painter (- n 1))))
(beside painter (below smaller smaller)))))
(define (up_split painter n)
(if (= n 0)
painter
(let ((smaller (up_split painter (- n 1))))
(below painter (beside smaller smaller)))))
(define (corner_split painter n)
(if (= n 0)
painter
(let ((up (up_split painter (- n 1)))
(right (right_split painter (- n 1))))
(let ((top_left (beside up up))
(bottom_right (below right right))
(corner (corner_split painter (- n 1))))
(beside (below painter top_left)
(below bottom_right corner))))))
(define (square_limit painter n)
(let ((quarter (corner_split painter n)))
(let ((half (beside (flip_horiz quarter) quarter)))
(below (flip_vert half) half))))
(define wave4 (flipped_pairs wave))
(define wave_rec (square_limit wave 2))
(wave4 normal_frame)
(wave_rec normal_frame)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment