Ticker

6/recent/ticker-posts

block vs lambda vs proc

What is a block in ruby. A block is a piece of code. However a block has to be attached to something.

1.# this wont' work
2.{ |x| puts x }
3.
4.# this won't work either
5.temp_block = {|x| puts x }

So how do I carry a piece of code from one place to another. Convert that piece of code into a proc.

1.temp_proc = Proc.new { puts "inside a block" }
2.temp_proc.call # inside a block

What is temp_proc here. Let’s find out the class of temp_proc.

1.temp_proc = Proc.new { puts "inside a block" }
2.puts temp_proc.class.to_s # Proc

What is lamda. Lamda is another way to convert a block of code into a proc.

1.temp_lambda = lambda { puts "inside a block" }
2.temp_lambda.call # inside a block

Let’s see what is the class of the temp_lambda.

1.temp_lambda = lambda { puts "inside a block" }
2.puts temp_lambda.class.to_s # Proc

temp_lambda is a Proc. It means both lambda and Proc.new create an instance of “Proc”. Then what’s the different between the two.

Differences between lambda and Proc. I know of two distinctions.

01.temp_proc = Proc.new {|a,b| puts "sum is #{a + b}" }
02.temp_lambda = lambda {|a,b| puts "sum is #{a + b}" }
03.
04.temp_lambda.call(2,3) #5
05.temp_lambda.call(2) #wrong number of arguments error
06.temp_lambda.call(2,3,4) #wrong number of arguments error
07.
08.temp_proc.call (2,3) # 5
09.temp_proc.call (2)
10.# nil can't be coerced. Since the second parameter was not supplied,
11.# proc.call supplied nil for the missing parameter
12.temp_proc.call(2,3,4) #5

First difference is that lambda.call wants the exact number of parameters to be supplied. Otherwise lambda will throw “wrong number of arguments error”. While Proc doesn’t demand that. For the missing params proc will supply “nil” and any extra parameter is ignored.

01.def learning_proc
02. temp_proc = Proc.new { return "creating proc" }
03. temp_proc.call # control leaves the method here
04. return "inside method learning_proc"
05.end
06.
07.puts learning_proc
08.
09.def learning_lambda
10. temp_lambda = lambda { return "creating lambda" }
11. temp_lambda.call # control doesn't leave the method here
12. return "inside method learning_lambda"
13.end
14.
15.puts learning_lambda

Second difference has to do with the usage of the word “return” in the context of proc. When there is a return keyword inside the block then proc treats that statement as returning from the method. Hence the statement return “inside method learning_proc” was never executed. Lambda behaves the way ruby behaves in general. The control goes to the next statement after the temp_lambda.call is finished.

The point to be noted is that not all the temp_proc.call will ensure the return from the method at that very instant. It’s the usage of the word return that is causing this behavior. This code will work just fine.

1.def learning_proc
2. temp_proc = Proc.new { "creating proc" }
3. temp_proc.call
4. return "inside method learning_proc"
5.end
6.
7.puts learning_proc

In ruby any method can accept a block and the methods do not need to do anything to accept this block of code. All the method has to do is to call “yield” and the piece of code will be invoked.

01.def learning
02. puts "learning ruby "
03. yield
04. puts "learning rake"
05.end
06.
07.learning {puts "learning rake" }
08.
09.# output is
10.#learning ruby
11.#learning rake
12.#learning rake

In the above example method “learning” did not have to do anything to accept the block. This feature is built into ruby.

However if the method wants to detect if a block is being passed to it or not, it can use the method block_given?.

01.def learning
02. puts "learning ruby "
03.
04. if block_given?
05. yield
06. else
07. puts "no block was passed"
08. end
09. puts "learning rake"
10.end
11.
12.learning
13.
14.# output is
15.#learning ruby
16.#no block was passed
17.#learning rake

There is another way to pass a block to a method: as an argument. However this argument must be the very last argument. It works like this.

1.def learning(&block)
2. block.call
3.end
4.
5.learning { puts "learning" }

In the above case there is an ampersand sign before the name “block”. That ampersand sign is important. By using that sign we are telling ruby that a block is being passed and convert that block into a proc and then set the variable name of the block as “block”. Now we can invoke “block.call”.

We can pass variables to a block. However it is important to understand the scope of the variable. Try this.

01.def thrice
02. x = 100
03. yield
04. yield
05. yield
06. puts "value of x inside method is #{x}"
07.end
08.
09.x = 5
10.puts "value of x before: #{x}"
11.thrice { x += 1 }
12.puts "value of x after: #{x}"
13.
14.#output
15.#value of x before: 5
16.#value of x inside method is 100
17.#value of x after: 8

This is something. A block will not touch variables defined inside the method. What happens if outer x is not defined.

01.def thrice
02. x = 100
03. yield
04. yield
05. yield
06. puts "value of x inside method is #{x}"
07.end
08.
09.puts "value of x before: #{x}"
10.thrice { x += 1 }
11.puts "value of x after: #{x}"
12.
13.#output
14.#undefined local variable or method `x' for main:Object (NameError)

Hopefully this provides some idea about blocks, procs and lambda. I intend to add some more cases later to this article.

Post a Comment

1 Comments

  1. Its a hilarious site. I have learned so many from here and I love the person who written this...

    ReplyDelete