CSC/ECE 517 Fall 2011/ch3 4b js: Difference between revisions
(→Blocks) |
(→Blocks) |
||
Line 33: | Line 33: | ||
>> 8 | >> 8 | ||
</pre> | </pre> | ||
In the example above, the instance variable <b> @block </b> is a closure. | |||
====Procs and Lambdas==== | ====Procs and Lambdas==== |
Revision as of 21:57, 16 October 2011
Closures
What is a Closure?
A closure is a block of code that can access the lexical environment of its definition. A closure has two properties:
- A closure can be passed as an object.
- A closure recalls the values of all the variables that were in scope when the function was created and is able to access those variables when it is called even though they may no longer be in scope.
A closure can be expressed succintly as a function pointer that references a block of executable code and the variables from the scope it was created.
Closures in Ruby
Ruby supports closures through use of Procedures, or simply procs, and lambdas, which are blocks.
Blocks
In Ruby, blocks are used to group statements usually enclosed by braces or between a do...end and occur solely in the source adjacent to a method call. Rule of thumb in Ruby is to use braces for blocks containing only a single line and do...end for multi-line blocks. The code within the block is not executed when encountered, instead, Ruby recalls the context of the block and then enters the method. Typically, the blocks passed into methods are anonymous objects that are created instantly, but they can be instantiated as a Proc object by using either the proc or lambda method. Proc objects are blocks of code that have been bound to a set of local variables. Once bound, the code may be called in different contexts and still access those variables.(7) Proc objects can be simply thought of as anonymous function objects. Proc objects can be stored, created at runtime, passed as arguments, retrieved, as well as, be returned as values. Consider the example below from lecture 5:
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)
>> 5 >> 8
In the example above, the instance variable @block is a closure.
Procs and Lambdas
When creating a lambda or proc, the object holds a reference to the executable block and bindings for all the variables used by the block. In Ruby, we can create a Proc object explicitly in three different ways:
Proc.new
Using this method involves simply passing in a block, which will return a Proc object that will run the code in the block when you invoke its call method.
pobject = Proc.new {puts “This is a proc object.”} pobject.call
proc method
In the Kernel module, we can use the proc method, which is available globally. In Ruby 1.9, it is equivalent to Proc.new, but in Ruby 1.8, it is equivalent to lambda. The difference between proc and lambda will be discussed later.
pobject = proc { puts “Inside the proc object” } pobject.call
lambda method
Similar to the proc method, it is globally available in the Kernel module, however, it will create a lambda Proc object.
pobject = lambda { puts “Inside the proc object” } pobject.call
procs vs. lambdas
One difference between the proc kernel method and the lambda kernel method is that the lambda kernel method re \\esults in an object that checks the number of its arguments, while a plain Proc.new does not: Another difference between procs and lambdas is their handling of control flow keywords, such as break and return. Consider the examples below (1). For instance, the first example below shows the difference in behavior using the return keyword. With the proc method, the return not only returns from the proc method, but also the enclosing method, which is shown when the last puts is not called.
def my_method puts "before proc" my_proc = Proc.new do puts "inside proc" return end my_proc.call puts "after proc" end my_method
user@user-ubuntu:/home/user/ruby$ ruby a.rb before proc inside proc
If we exchange the proc for the lambda method, the return only returns from the lambda method and NOT the enclosing method, thus, it continues on to execute the last puts .
def my_method puts "before proc" my_proc = lambda do puts "inside proc" return end my_proc.call puts "after proc" end my_method
user@user-ubuntu:/home/user/ruby$ ruby a.rb before proc inside proc after proc
If we go back to the proc method, and use the break keyword in lieu of return . However, instead of returning from the proc method and the enclosing method, we are issued a LocalJumpError . Since break keywords are usually used to fall out of an iteration, but it is not contained within an iteration, Ruby throws an error.
def my_method puts "before proc" my_proc = Proc.new do puts "inside proc" break end my_proc.call puts "after proc" end my_method
user@user-ubuntu:/home/user/ruby$ ruby a.rb before proc inside proc a.rb:64:in `block in my_method': break from proc-closure (LocalJumpError) from a.rb:66:in `call' from a.rb:66:in `my_method' from a.rb:70:in `<main>'
Let us revisit the lambda method, but this time, we will use the break keyword. You will notice that the break is treated like the return keyword, where the execution is dumped out of the lambda, but continues to execute the rest of the enclosing method.
def my_method puts "before proc" my_proc = lambda do puts "inside proc" break end my_proc.call puts "after proc" end my_method
user@user-ubuntu:/home/user/ruby$ ruby a.rb before proc inside proc after proc
Why use Closures?
Closures not only enable programmers to write logic with less code, they are also useful in iterating over lists and encapsulation operations that require setup and clean-up afterwards.
Currying
Object-Oriented Programming in Ruby
Ruby is the ideal Object Oriented Programming Language, which has four essential properties:
- Data Encapsulation
- Data Abstraction
- Polymorphism
- Inheritance
An object-oriented program consists of classes and objects. An object consists of state and related behavior, where its state is contained within fields and allows access to its behavior through use of methods. These methods can alter an object's state and serve as the facilitator for communication between objects. Classes are defined as the building blocks from which objects are created and will be explained further as we progress through the four properties through the subsequent sections.
Classes
Attributes
Inheritance
Access Control
Abstract Methods
References
- (1)http://www.skorks.com/2010/05/ruby-procs-and-lambdas-and-the-difference-between-them
- (2)http://samdanielson.com/2007/9/6/an-introduction-to-closures-in-ruby
- (3)http://en.wikipedia.org/wiki/Closure_(computer_science)
- (4)http://en.wikipedia.org/wiki/Continuation-passing_style
- (5)Flanagan, D and Matsumoto, Y, (2008). The Ruby Programming Language, O’Reilly Media, Inc.,
- (6)Fitzgerald Michael, (2007). Learning Ruby, O’Reilly Media, Inc., (example)
- (7)http://blog.mostof.it/why-ruby-part-two-blocks-and-closures/