class Point
attr_reader :x, :y
def initialize(x,y)
@x = x
@y = y
end
def distFromOrigin
Math.sqrt(@x * @x + @y * @y) # why a module method? Less OO :-(
end
def distFromOrigin2
Math.sqrt(x * x + y * y) # uses getter methods
end
end
# design question: "Is a 3D-point a 2D-point?"
class ThreeDPoint < Point
attr_reader :z
def initialize(x,y,z)
super(x,y)
@z = z
end
def distFromOrigin
d = super
Math.sqrt(d * d + @z * @z)
end
def distFromOrigin2
d = super
Math.sqrt(d * d + z * z)
end
end
#hmm, without overrding "why not" just add a method to Point instead?
# design choice, really (but adding would affect ThreeDPoint too!)
class ColorPoint < Point
attr_reader :color
attr_writer :color # just our choice to make mutable like this
# (say p.color = "green" rather than needing
# p.myColorSetter("green") which does @color="green" in its body)
def initialize(x,y,c="clear") # or could skip this and color starts unset
super(x,y)
@color = c
end
end
class PolarPoint < Point
# interesting, but not calling super constructor, no x and y field
# (in Java or Smalltalk would just have unused x and y fields)
def initialize(r,theta)
@r = r
@theta = theta
end
def x
@r * Math.cos(@theta)
end
def y
@r * Math.sin(@theta)
end
def x= a
b = y # avoid multiple calls, but fine either way
@theta = Math.tan(b / a)
@r = Math.sqrt(a*a + b*b)
self
end
def y= a
b = x # avoid multiple calls, but fine either way
@theta = Math.tan(b / a)
@r = Math.sqrt(a*a + b*b)
self
end
def distFromOrigin
r
end
# distFromOrigin2 already works!!!
end
#### separate topic, some basics of iterators and blocks
def play
# many library methods "take a block", which is essentially a closure
# avoids many uses of explicit loops
# (more concise, separates traversal from processing)
3.times { puts "hi" }
[4,6,8].each { puts "hi" } # can "ignore" argument
y = 7
[4,6,8].each { |x|
y = y + x
puts y
}
[4,6,8].map { |x| x + 1 }
# lambda is a built-in method that returns a Proc that is
# _exactly_ a closure, you call it with the call method
cl = lambda {|z| z * y}
q = cl.call(9)
puts q
# since Proc values can be passed around, often
# what you need (e.g., on homework) (see foo, bar below)
puts (foo (lambda { |x| x + x }))
# but if only the immediate callee needs the block, more
# convenient to use the "yield" language feature
puts (foo2 { |x| x + x })
# learn on your own only if you want: using "& argument"
# so caller uses a block (not a Proc) but callee gets a Proc
# (how lambda is implemented -- just a method of Object)
end
def foo fun
eight = fun.call 4
twelve = bar fun
eight + twelve
end
def bar f
f.call 6
end
def foo2
eight = yield 4
twelve = yield 6
eight + twelve
end