Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Principle of Succinctness
The supreme design goal of Ruby
Makes programmers happy and makes Ruby easy to learn
Examples
What class does an object belong to ? – can ask
o.class (returns the class object)
Is it Array#size or Array#length?
Either one - same method – they’re aliased
Is this good or bad design?
Array operators? – if it makes sense, they are defined.
diff = ary1 – ary2 (removes elements of ary2 from ary1)
union = ary1 + ary2 (concatenate arrays)
intersection = ary1 & ary2 (elements common to both)
also known as the Principle of Least Effort
Motivation…
We don’t like to waste time
The quicker we program, the more we
accomplish
Sounds reasonable enough, right?
Less code means less bugs
Common needs are built-in.
All classes derived from Object including Class (like
Java) but there are no primitives (not like Java at
all). EVERYTHING is an Object.
Ruby uses single-inheritance
What are the dangers of multiple inheritance?
Mixins give you the power of multiple inheritance
without the headaches
Modules allow addition of behaviors to a class
Reflection is built in along with lots of other highly
dynamic metadata features
Things like ‘=‘ and ‘+’ that you might think are
operators are actually methods (like Smalltalk)
a mixin is a class that is mixed with a
module. In other words the implementation
of the class and module are joined,
intertwined, combined, etc.
a module as a degenerate abstract class. A
module can’t be instantiated and no class can
directly extend it but a module can fully
implement methods.
Stringify makes use
# Convert a integer value to English. of a @value instance
02.module Stringify variable.
03. # Requires an instance variable The class that will be
songList.each do |aSong|
aSong.play
end
Duck Typing
Based on signatures, not class inheritance
In Ruby, we rely less on the type (or class) of an object and
more on its capabilities.
Duck Typing is a type of dynamic typing in which the object’s
current set of methods and properties determines the valid
semantics, rather than its inheritance from a particular class
or implementation of a specific interface.
The name of the concept refers to the duck test, attributed to
James Whitcomb Riley which may be phrased as follows:
"when I see a bird that walks like a duck and swims like a duck
and quacks like a duck, I call that bird a duck."
# Check whether the object defines the to_str method
class Goose
def honk If you are calling
'Honk!'
end “swim”, Duck and
def swim
'Splash splash splash...' Goose are
end
end interchangeable.
class DuckRecording
def quack
play
end Method isn’t
def play expecting a specific
'Quack!'
end type JUST one with
end
the needed
functionality.
Dynamic Dispatch
A key concept of OOP: methods are actually messages that
are sent to an object instance
It is a runtime decision to decide which code to execute
Can’t be determined statically (method could have been
overridden dynamically)
foobar = Array.new
foobar.class # => Array
foobar.size # => 0
foobar = Array.new
def foobar.size // Adds a size method ONLY for the instance
“Infinity and beyond" ;
end
Languages with weak or no typing
systems often carry a dispatch table as
part of the object data for each object.
This allows instance behavior as each
instance may map a given message to a
separate method.
Languages with strong (static) typing use
a virtual table which defines method to
code mapping. Instances of the type
store a pointer to this table.
Suppose a program contains several classes in
an inheritance hierarchy: a superclass Cat, and
two subclasses, HouseCat and Lion. Class Cat
defines a virtual function named speak, so its
subclasses may provide an appropriate
implementation (e.g. either meow or roar).
When a Cat variable calls speak, we need to be
able to tell which method to call.
dispatch table contains addresses of methods
Use same layout for type-compatible methods
– so we look for address at a constant offset
Dynamic Behavior
◦ Reflection
◦ Scope Reopening (Kind of like AOP)
◦ Eval
◦ Breakpoint debugger
One of the many advantages of dynamic
languages such as Ruby is the ability to
introspect---to examine aspects of the
program from within the program itself.
Some call this feature reflection.
We might discover:
◦ what objects it contains,
◦ the current class hierarchy,
◦ the contents and behaviors of objects, and
◦ information on methods.
Everything is an object EVEN the class.
5.class #=> Fixnum
"hello".class #=> String
class Foo
end
Foo.class #=> Class
Foo is a constant known to the system as a
class
Have you ever craved the ability to traverse all the living objects
in your program? Ruby lets you perform this trick with
ObjectSpace::each_object .
For example, to iterate over all objects of type Numeric, you'd
write the following.
a = 102.7
b = 95 # Won't be returned
c = 12345678987654321 # Won't be returned
count = ObjectSpace.each_object(Numeric) {|x| p x }
puts "Total count: #{count}“
Produces:
102.7
3.14159265358979
2.71828182845904
2.22044604925031e-16
1.79769313486232e+308
4.9e-324
Total count: 6
Hey, where did those last numbers come from? We didn't define
them in our program. The Math module defines constants for e
and PI; since we are examining all living objects in the system,
these turn up as well.
Right click on Project name,
Select Properties
Click Run
Define Ruby Option
For instance, we can get a list of all the methods to
which an object will respond.
r=1..10
num = 14
list = r.methods
list.length # 60
list[0..3]
# [exclude_end?, to_a, include?, member?]
r.respond_to?("frozen?") #>> true
r.respond_to?("hasKey") #>> true
num.instance_of? Fixnum #>> true
one_class = Fixnum
begin
print one_class
one_class = one_class .superclass
print " < " if one_class
end while one_class
puts p Fixnum.ancestors
puts self.class # scripts are automatically in Object
puts 3.class # numbers are Fixnum
Produces
Fixnum<Integer<Numeric<Object
[Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel]
Object
Fixnum
trane="John Coltrane".method(:length)
miles="Miles Davis".method("sub")
puts trane.call
puts miles.call(/iles/,'.')
trane=%q{"John Coltrane".length}
miles=%q{"Miles Davis".sub(/iles/,'.')}
puts eval trane
puts eval miles
produces 14
M. Davis
14
M. Davis
class CoinSlot
def initialize(amt)
At instance variable begins with @
and its scope is confined to
@amt=amt whatever object self refers to
$here=binding
end A global variable begins with $
k1 = Demo.new(99)
b1 = k1.getBinding
k2 = Demo.new(-3)
b2 = k2.getBinding
class ClockRadio
def on!
@on = true
end
def on?
@on
end
end
Clock Radio is complete,
Sometime later we can redefine methods and add new ones:
class ClockRadio
alias :old_on! :on!
def on!
old_on! # Calling original code
@display_time = true
end
def display_time?
@display_time
end
end
The advantage you gain from being able to
reopen classes is that you can patch the
framework you’re using unobtrusively in order
to fix bugs or improve performance.
Violates OO principle by allowing anyone to add
members and methods to an existing class, even
outside of the original class definition.
The methods thus added have full access to all
other members and methods, including private
ones.
This is a “violation of the Open/Closed Principle,”
in that the original class is not kept closed
against modifications.
It certainly isn’t pure. There really isn’t a Grand
Unifying Idea. It clearly copies features from very
different languages and allows for a variety of
programming styles.
A closure is a nameless function.
It has code to run (the executable)
It has state around the code (the scope)
Useful:
Tutoring – allows users to run their code in a
protected environment
evaluating math expression (like in spreadsheet)
more flexible than a parser as can call functions that
were just created.
implemented with the same interpreter as normal
code
Dangers:
input = # ... read from some form
eval input
What if they input 'rm -rf *‘ ?