
here.Before you begin, you need to install the following software on your
computer. The installation instruction of JDK and NetBeans is
described in
Exercise 0
below.
download)
Learning points
Tasks to be performed:| # # Block can be represented in two different formats # puts "----First format of code block containing code fragment between { and }" [1, 2, 3].each { |n| puts "Number #{n}" } puts "----Second format of code block containing code fragment between do and end" [1, 2, 3].each do |n| puts "Number #{n}" end |
Explanation of the code: The each is a method of Array class as shown Array Ruby doc.
A block is attached (supplied) to the
each method. The each method
calls the block once for each element in self, passing that element as
a parameter. This is an example of a built-in method of Ruby language
which takes a block. In the subsequent steps, you are going to
define your method which takes a block.
For your own study: Move the mouse
to the each and press
CTRL+Space and read the document of each
method.
| ----First format of code block
containing code fragment between { and } Number 1 Number 2 Number 3 ----Second format of code block containing code fragment between do and end Number 1 Number 2 Number 3 |
| # # The goal of this exercise is to see how # a supplied block is executed through "yield" # keyword. # puts "----Define MyClass which invokes yield" class MyClass def command() puts "This is a message from the command method of MyClass " # yield will execute the attached block to the method yield() end end puts "----Create object instance of MyClass" m = MyClass.new puts "----Call command method of the MyClass passing a block" m.command {puts "Hello World!"} puts "----Call command method of the MyClass passing a block" m.command {puts "Life is good!"} puts "----Call command method of the MyClass passing a block" # Sometimes you will see blocks that seem 'nested', for example, here # the 'command' yields to a block, who calls method 'each', who yields # (again!) to an inner block" m.command do [1, 2, 3].each do |n| puts "Number #{n}" end end puts "----Call command method of the MyClass passing a block" m.command { [1, 2, 3].each { |n| puts "Number #{n}" } } |
Explanation of the code: When command method of the MyClass is
invoked, it is attached (or supplied/passed/sent) with a block, for
example,
{puts "Hello World!"}.
Now this attached block will be executed where the yield() method is called within the command method. Hence, the
result will be| This
is a message from the command method of MyClass Hello World! |
For your own study: Move the
cursor over yield and press
CTRL+Space. Observe the document dialog box of the yield gets displayed.
| ----Define MyClass which invokes
yield ----Create object instance of MyClass ----Call command method of the MyClass passing a block This is a message from the command method of MyClass Hello World! ----Call command method of the MyClass passing a block This is a message from the command method of MyClass Life is good! ----Call command method of the MyClass passing a block This is a message from the command method of MyClass Number 1 Number 2 Number 3 ----Call command method of the MyClass passing a block This is a message from the command method of MyClass Number 1 Number 2 Number 3 |
| puts "----Define MyClass that
yields the passed code block" class MyClass def command1() # yield will execute the supplied block yield(Time.now) end def command2(my_parameter) puts "**Star command says: " # yield will execute the supplied block yield(my_parameter) end end puts "----Create an object instance of MyClass" m = MyClass.new puts "----Call command1 method of the MyClass" m.command1() {|x| puts "Current time is #{x}"} puts "----Call command2 method of the MyClass" m.command2(1) {|x| puts "Hello World! is called with number #{x}" } m.command2(2) {|i| puts "Number #{i} is passed."} m.command2(3) {|i| puts "Number #{i} is passed."} m.command2("something") {|x| puts x} m.command2("something") {|t| puts t} |
| ----Define MyClass that yields
the passed code block ----Define MyClass that yields the passed code block ----Create an object instance of MyClass ----Call command1 method of the MyClass Current time is Mon Jul 14 15:23:20 -0400 2008 ----Call command2 method of the MyClass **Star command says: Hello World! is called with number 1 **Star command says: Number 2 is passed. **Star command says: Number 3 is passed. **Star command says: something **Star command says: something |
Explanation of the code: The block
can take parameters, which are passed from the yield(<parameters>) method.
In this example, the yield(my_parameter)
method passes my_parameter
as a parameter. In the block, the passed parameters(or arguments)
are represented as variables between the two vertical bars, |<variables>|.| puts "---Define a method called
testyield" def testyield yield(1000, "Sang Shin") yield("Current time is", Time.now) end puts "----Call testyield method" testyield { |arg1, arg2| puts "#{arg1} #{arg2}" } |
| ---Define a method called
testyield ----Call testyield method 1000 Sang Shin Current time is Mon Jun 30 09:14:56 -0400 2008 |
Explanation of the code: When the yield(1000, "Sang Shin") is invoked
within the testyield method,
two parameters, 1000 and "Sang Shin" are passed to the block
as two arguments, which are represented as arg1 and arg2.
Learning points
Tasks to be performed in this
exercise| # Proc objects are blocks of
code that have been converted # to callable objects. puts "----Create a Proc object and call it" say_hi = Proc.new { puts "Hello Sydney" } say_hi.call puts "----Display the class of Proc object" puts say_hi.class puts "----Create another Proc object and call it" Proc.new { puts "Hello Boston"}.call |
| ----Create a Proc object and
call it Hello Sydney ----Display the class of Proc object Proc ----Create another Proc object and call it Hello Boston |
Learning point: The proc
method converts a block into a Proc object.| puts "----Define a method that
returns a Proc object" def my_hello return proc { |x, y| puts "Hello #{x} #{y}" } end puts "----Execute the block" my_variable = my_hello my_variable.call("Young Shin", Time.now) |
| ----Define a method that returns
a Proc object ----Execute the block Hello Young Shin Tue Jul 15 18:01:33 -0400 2008 |
Learning point: A method returns a
Proc object as a return value.
Learning point: This code also
shows how Proc object is used as a Closure. Note that Proc
objects (Procs) 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. In this example, when gen_times(3) is invoked, the loal
variable factor is to 3 and the code remembers
that. So times3.call(12) is called later on, the code will use
the value of the factor variable, 3.| puts "----Define a method that
returns Proc object as a return value" def gen_times(factor) Proc.new {|n| n*factor } end puts "----Assign Proc object to local variables" times3 = gen_times(3) # Block has factor variable set to 3 times5 = gen_times(5) # Block has factor variable set to 5 puts "----Execute the code block passing a parameter" puts times3.call(12) #=> 36 because 12 * 3 (factor) = 36 puts times5.call(5) #=> 25 because 5 * 5 (factor) = 25 puts times3.call(times5.call(4)) #=> 60 because (4 * 5 ) * 3 (factor) = 60 |
| ----Define a method that returns
Proc object as a return value ----Assign Proc object to local variables ----Execute the code block passing a parameter 36 25 60 |
Learning point: Proc object can be
passed around as an arugment.| puts "----Create Proc objects" proc_a = Proc.new {|x| puts x} proc_b = Proc.new {|x| puts x.reverse } puts "----Define a method that receives Proc object as an argument, it also calls block" def foo (proc_obj, str) proc_obj.call(str) end puts "----Call a method that passes a Proc object as an arugment" foo(proc_a, 'Sang Shin') foo(proc_b, 'Sang Shin') |
| ----Create Proc objects ----Define a method that receives Proc object as an argument ----Call a method that passes a Proc object as an arugment Sang Shin nihS gnaS |
| # Equivalent to Proc.new, except
the Proc object built using # lambda checks the number of parameters passed when # called. puts "----Create a Proc object through lambda and call it" say_hi = lambda { puts "Hello Sydney" } say_hi.call puts "----Create another Proc object through lambda and call it" lambda { puts "Hello Boston"}.call |
| ----Create a Proc object and
call it Hello Sydney ----Create another Proc object and call it Hello Boston |
| # The resulting Proc object
(from using lambda) checks the # number of parameters passed when called, if not match, # throws an exception puts "----Create Proc object using lambda" lamb = lambda {|x, y| puts x + y} puts "----Create Proc object using Proc.new" pnew = Proc.new {|x, y| puts x + y} puts "----Send 3 arguments, should work" pnew.call(2, 4, 11) puts "----Send 3 arguments, throws an ArgumentError" lamb.call(2, 4, 11) |
| ----Create Proc object using
lambda ----Create Proc object using Proc.new ----Send 3 arguments, should work 6 ----Send 3 arguments, throws an ArgumentError C:\handsonlabs\ruby_blocks\samples\RubyBlock_lambda_diffFromProcNew1\lib\main.rb:15:in `call': wrong number of arguments(3 for 2) (ArgumentError) from C:\handsonlabs\ruby_blocks\samples\RubyBlock_lambda_diffFromProcNew1\lib\main.rb:15 |

| # Another difference between
lambda and Proc.new is in the way # returns are handled from the Proc. A return from Proc.new # returns from the enclosing method (acting just like a return # from a block). A return from lambda acts more conventionally, # returning to its caller. def foo f = Proc.new { return "return from foo from inside proc" } f.call # control leaves foo here return "return from foo" end def bar f = lambda { return "return from lambda" } f.call # control does not leave bar here return "return from bar" end puts foo # prints "return from foo from inside proc" puts bar # prints "return from bar" |
| return from foo from inside proc return from bar |
In this exercise, you have learned
how to use a block of Ruby language.
Learning points
Tasks to be performed:| # The ampersand operator can be
used to explicitly convert # between blocks and Procs in a couple of cases. It is worthy # to understand how these work. # If an ampersand (&) is prepended to the last argument in # the argument list of a method, the block attached to this # method is converted to a Proc object and gets assigned to # that last argument. puts "----Define a method that receives a block as a Proc object" def m1(a, &f) f.call(a) end puts "----Define a method that receives a block" def m2(a) yield a end puts "----Call a method passing a block" m1('Seoul') { |x| puts "I dream of #{x}" } puts "----Call a method passiing a block" m2('Milan') { |x| puts "I dream of #{x}" } |
| ----Define a method that
receives a block as a Proc object ----Define a method that receives a block ----Call a method passing a block I dream of Seoul ----Call a method passiing a block I dream of Milan |
| # Another use of the
ampersand
is the other-way conversion - # converting a Proc into a block. This is very useful because # many of Ruby’s great built-ins, and especially the iterators, # expect to receive a block as an argument, and sometimes it’s # much more convenient to pass them a Proc. puts "----Create a Proc object" say_hi = Proc.new { |x| puts "#{x} Hello Korea" } say_hi.call puts "----Define a method which expects Proc object as an argument" def do_it_with_call(&f) f.call rand(10) end puts "----Define a method which expects a block NOT Proc object" def do_it_with_yield yield rand(10) if block_given? end do_it_with_call &say_hi # => 6 Hello Korea do_it_with_yield &say_hi # => 2 Hello Korea |
| ----Create a Proc object Hello Korea ----Define a method which expects Proc object as an argument ----Define a method which expects a block NOT Proc object 6 Hello Korea 3 Hello Korea |
Learning points
Tasks to be performed:| puts
"----Define a method which takes block as a Proc object argument" def my_method(count, &my_block) value = 1 # Execute the block "count" times while updating the vlaue i 1.upto(count) do |i| value = value * i my_block.call(i, value) end end puts "----Invoke the method passing a code block" my_method(5) do |i, result| puts "my_method(#{i}) = #{result} " end |
| ----Define a method which takes
block as a Proc object argument ----Invoke the method passing a code block my_method(1) = 1 my_method(2) = 2 my_method(3) = 6 my_method(4) = 24 my_method(5) = 120 |
| # An example where block is
passed as an initialization argument class Repeater def initialize(&block) # Save the Proc object into an instance variable @block = block @count = 0 end def repeat @count +=1 @block.call(@count) end end puts "---- Pass a block as an intialization argument" repeater = Repeater.new do |count| puts "You called me #{count} times" end puts "---- Call the repeat method 5 times" 5.times do repeater.repeat end |
| ---- Pass a block as an
intialization argument ---- Call the repeat method 5 times You called me 1 times You called me 2 times You called me 3 times You called me 4 times You called me 5 times |
Learning points
Tasks to be performed| # The interesting thing is that
it's more than just a chunk of code. # Associated with a block (and hence a Proc object) is all the context # in which the block was defined: the value of self, and the methods, # variables, and constants in scope. Part of the magic of Ruby is # that the block can still use all this original scope information # even if the environment in which it was defined would otherwise # have disappeared. In other languages, this facility is called a # closure. # Let's look at a contrived example. This example uses the method proc, # which converts a block to a Proc object. def nTimes(aThing) return proc { |n| aThing * n } end puts "----Create a Proc object by calling nTimes method. " # The aThing is set to value 23 in a block. Even if nTimes method is # finished, the aThing variable in the block still contains 23.. p1 = nTimes(23) puts "----Execute the block" # Now execute the block. Note that the aThing is still set to # 23, so the results is 69 and 92 puts p1.call(3) # 69 puts p1.call(4) # 92 puts "----Create a Proc object by calling nTimes method. " # The aThing is set to value "Hello " in a block. Even if nTimes method is # finished, the aThing variable in the block still contains "Hello ". p2 = nTimes("Hello ") puts "----Execute the block" # Now execute the block. Note that the aThing is still set to # "Hello ", so the results is "Hello Hello Hello " puts p2.call(3) # "Hello Hello Hello " |
| ----Create a Proc object by
calling nTimes method. ----Execute the block 69 92 ----Create a Proc object by calling nTimes method. ----Execute the block Hello Hello Hello |
| # Blocks are like closures,
because they can refer to variables from # their defining context: def thrice yield yield yield end x = 5 puts "value of x before: #{x}" thrice { x += 1 } puts "value of x after: #{x}" |
| value of x before: 5 value of x after: 8 |
| # A block refers to variables in
the context it was defined, not the # context in which it is called: def thrice_with_local_x x = 100 yield yield yield puts "value of x at end of thrice_with_local_x: #{x}" end x = 5 thrice_with_local_x { x += 1 } puts "value of outer x after: #{x}" #=> 8 |
| value of x at end of
thrice_with_local_x: 100 value of outer x after: 8 |
| # A block only refers to
*existing* variables in the outer context; if # they don't exist in the outer, a block won't create them there: def thrice yield yield yield end thrice do # note that {...} and do...end are completely equivalent y = 10 puts "Is y defined inside the block where it is first set?" puts "Yes." if defined? y end puts "Is y defined in the outer context after being set in the block?" puts "No!" unless defined? y |
| Is y defined inside the block
where it is first set? Yes. Is y defined inside the block where it is first set? Yes. Is y defined inside the block where it is first set? Yes. Is y defined in the outer context after being set in the block? No! |
| # OK, so blocks seem to be like
closures: they are closed with respect to variables defined in the
context # where they were created, regardless of the context in which they're called. # # But they're not quite closures as we've been using them, because we have no way to pass them around: # "yield" can *only* refer to the block passed to the method it's in. # # We can pass a block on down the chain, however, using &: def thrice yield yield yield end def six_times(&block) thrice(&block) thrice(&block) end x = 4 six_times { x += 10 } puts "value of x after: #{x}" |
| value of x after: 64 |