CSC/ECE 517 Fall 2011/ch4 4b ds

From Expertiza_Wiki
Jump to navigation Jump to search

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 ones 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 prove 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.




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/