CSC/ECE 517 Fall 2011/ch4 4b ds: Difference between revisions
No edit summary |
|||
Line 245: | Line 245: | ||
The downside of duck typing is that, the absence of the static type checking may increase the execution time. This is also dynamic typing because type information is determined at run-time. Other well-known languages which heavily use dynamic typing are Python, PHP, Perl, Objective-C, etc. | The downside of duck typing is that, the absence of the static type checking may increase the execution time. This is also dynamic typing because type information is determined at run-time. Other well-known languages which heavily use dynamic typing are Python, PHP, Perl, Objective-C, etc. | ||
== Conclusion == | |||
We started by listing out the collections present in Java which uses JCF, and C++ which uses STL. Then we made a comparison of the collections in C++ , Java and Ruby. Each language's implementation of collections has its pros and cons. Ultimately it is the developer who chooses which one to use, and this is based on the needs of his application. In general C++ STL provides collections that are fast, Java JCF provides collections that are safe, while in Ruby, the collections can be written with minimal code as they are built into the language. | |||
== References == | == References == |
Revision as of 17:57, 20 October 2011
Wiki textbook chapter on OOLS Lecture 5. Covers the basic of closures and blocks, currying and OOP in Ruby.
Introduction
Closures
A closure is a block of code that “closes over”. This means it can access the lexical environment of its definition. A closure may be defined in one scope and get called outside this scope. Thus a closure retains the values of all the variables that were in scope when the closure was defined. Closures help in making the code short such that one can do more with less code. Ruby supports closures through Blocks and Procs.
Blocks
A block is a set of Ruby statements either between braces or a do/end pair, and appear only immediately after a method invocation. Ruby uses the following standard - braces for single-line blocks and do/end for multiline blocks.
{ puts "Hello World" } # braces block do { puts "Hello World 1" } # do/end block { puts "Hello World 2" } { puts "Hello World 3" } end
Blocks can be instantiated as a Proc object using the Proc or lambda method. The block is then bound to a set of local variables. Once bound, the block code may be called in different contexts through these set variables.Using the example mentioned in lecture 5 in class.
# Class to generate adders class AdderGen def initialize(n) @block = lambda {|a| n + a} end def add(a) @block.call a end end twoAdder = AdderGen.new 2 incrementer = AdderGen.new 1 puts incrementer.add(4) puts twoAdder.add(6)
Output 5 8
@block is the Ruby closure that gets initialized when the lambda method gets called through the class initialize method. The block remembers the parameter with which the initialize method was called even after the initialize method exits. This value of the block is used during the add method invocation.
Procs And Lambdas
There are three ways to create a Proc object in Ruby.
Proc.new
Proc.new takes in a block and returns a Proc object which will run the code in the block when call method is invoked.
proc_object = Proc.new {puts "Create Proc.new object"} proc_object.call
Proc method
The proc method is equivalent to Proc.new in Ruby 1.9, but in Ruby 1.8 it is equivalent to lambda.
proc_object = proc {puts "Create proc from Proc method"} proc_object.call
Lambda method
This creates a proc object using the lambda method
proc_object = lambda {puts "Create proc from Lambda method"} proc_object.call
Currying
Currying is the concept of creating a new function out of an existing function by fixing the value of some of its input parameters. Currying thus makes a generic function more specific to one's needs. Consider the following example where we have a Proc for addition of two numbers. Using currying we can fix the value of the first argument so that when the curried function is called later on with only one argument, it can use the earlier fixed value as the other argument for its addition. Ruby 1.9 allows the creation of a curry-able proc by calling the curry method on it.
add = lambda {|a,b| a + b} puts add[1,2] curried_add = add.curry add_to_ten = curried_add[10] add_to_twenty = curried_add[20] puts add_to_ten[5] puts add_to_twenty[5]
Output 3 15 25
Class
Classes are templates for creating object instances. Class methods are methods that are called on a class and instance methods are methods that are called on an instance of a class. Here is a quick example and then we’ll go into a bit more detail.
class BookInStock def initialize(isbn, price) @isbn = isbn @price = Float(price) end delf self.book puts “Class method” end end
initialize is a special method in Ruby programs. When you call Song.new to create a new Song object, Ruby creates an uninitialized object and then calls that object's initialize method, passing in any parameters that were passed to new. This gives you a chance to write code that sets up your object's state.
Attributes
An object's instance variables are its attributes, the things that distinguish it from other objects of the same class. It is important to be able to write and read these attributes; doing so requires methods called attribute accessors.
Attr_reader: Creates instance variables and corresponding methods that return the value of each instance variable.
Attr_writer: Creates an accessor method to allow assignment to the attribute
Attr_accessor: Creates a reader and a writer method for each symbol passed as an argument. These methods provide access to the underlying instance variables of the name name (with a leading @ sign).
class BookInStock attr_reader :title attr_writer :price attr_accessor : isbn end
Shortcut Effect attr_reader :title def title; @title; end attr_writer :price def price=(price); @price=price; end attr_accessor :isbn attr_reader :isbn; attr_writer :isbn
Inheritance
Inheritance is one of the pillars of object-oriented programming. Inheritance allows you to create a class that is a refinement or specialization of another class. This class is called a subclass of the original, and the original is a superclass of the subclass.
For example here Ale is-a subclass of Beer and Beer is-a superclass of Ale. Inheritance allows a subclass to have all the methods defined in the superclass and hence promotes code reuse. In addition subclass can override the methods which are much more specialized than superclass. Ale class now gets the intake method defined in the Beer and also it has another method defined taste which varies from one subclass to another. Also it’s important to note that all classes in Ruby inherit from the Object class and hence all the methods declared in the Object class are now implicitly part of any class we declare.
class Beer def intake puts "blurp blurp" end end class Ale < Beer def taste puts "Sweet, tasty tasty" end end beverage = Ale.new beverage.taste beverage.intake
Access control
Ruby allows having control over the methods defined in the class.
There are three levels of protection provided by Ruby:
1. Public methods can be called by any class. No access control is enforced. In Ruby, methods are public by default ( except initialize, which is private). 2. Protected methods can be used by the class which defines them and the subclass which inherit these classes. 3. Private methods can be called only in the context of the current object. You can’t invoke other objects private methods.
The below example shows some details about access control.
class AccessControlExample def method1 # default is “public” #... end protected # subsequent methods will be “protected” def method2 # will be “protected” #... end private # subsequent methods will be “private” def method3 # will be “private” #... end public # subsequent methods will be “public” def method4 # and this will be “public” #... end end
Abstract methods
Ruby doesn’t support the concept of abstract methods. This is because Ruby is dynamically typed and you can add methods on the fly. If you want to simulate the notion of abstract method, you can do the following,
class Animal def talk raise “NotImplementedError.new("Method not implemented") end end
class Dinosaur < Animal def talk puts “Roar! Roar” end end
Here Dinosaur class implements talk method. If you don’t implement the talk method in Dinosaur, an attempt to instantiate the class throws an error. However the notion of abstract methods in the Ruby are not used heavily.
Duck typing (Unbounded Polymorphism)
In Ruby, the class is never the type. Instead, the type of an object is defined more by what that object can do which is determined by the methods and properties rather than inheritance from particular class. In Ruby, we call this duck typing. If an object walks like a duck and talks like a duck, then the interpreter is happy to treat it as if it were a duck.
Lets look at the example below.
There are 4 classes defined which are not related in any way. The classes Frog, Dinosaur, Table have a talk method defined while the class Bird has chirp method defined. Since the Table class has a talk method we can say it has animal even though by reality it does not belong which illustrates Duck typing in Ruby. Since Bird class does not implement the talk method it is not classified as of type animal.
class Frog def talk puts "ribbit, ribbit" end end
class Dinosaur def talk puts "Roar" end end
class Table def talk puts "chss, chss" end end
class Bird def chirp puts “kuckoo” end end
class Test def test_animal(an) an.talk end end
t = Test.new t.test_animal(Frog.new) t.test_animal(Dinosaur.new) t.test_animal(Table.new) t.test_animal(Bird.new)
The downside of duck typing is that, the absence of the static type checking may increase the execution time. This is also dynamic typing because type information is determined at run-time. Other well-known languages which heavily use dynamic typing are Python, PHP, Perl, Objective-C, etc.
Conclusion
We started by listing out the collections present in Java which uses JCF, and C++ which uses STL. Then we made a comparison of the collections in C++ , Java and Ruby. Each language's implementation of collections has its pros and cons. Ultimately it is the developer who chooses which one to use, and this is based on the needs of his application. In general C++ STL provides collections that are fast, Java JCF provides collections that are safe, while in Ruby, the collections can be written with minimal code as they are built into the language.
References
[1] http://en.wikipedia.org/wiki/Closure_(computer_science)
[2] http://www.skorks.com/2010/05/closures-a-simple-explanation-using-ruby/
[3] http://www.skorks.com/2010/05/ruby-procs-and-lambdas-and-the-difference-between-them/