A summary of the differences among block, proc and lambda in Ruby

Time:2019-11-27

In the rule engine, Ruby’s closures are frequently used, and there are block, proc and lambda, which are confusing. In order to understand the code in depth, I have learned the similarities and differences of ruby closures again, especially the usage of block, proc and lambda. This week’s notes will share my experience with you.

Closures are one of the special advantages of ruby compared with other languages. Many languages have closures, but only Ruby makes the most of them. One of Ruby’s philosophy: there are many solutions to the same problem in reality, so there are many solutions in ruby, so there are many ways to use closures.

Let’s look at a code example:

Example 1:

Copy codeThe code is as follows:
def foo1
  yield
end

def foo2(&b)
  b.call if b
end

foo1 { puts “foo1 in block” }
foo2 { puts “foo2 in block” }
proc = Proc.new { puts “foo in proc” }
foo1(&proc)
foo2(&proc)
lambda_proc = lambda { puts “foo in lambda” }
foo1(&lambda_proc)
foo2(&lambda_proc)

Output:

Copy codeThe code is as follows:
》foo1 in block
》foo2 in block
》foo in proc
》foo in proc
》foo in lambda
》foo in lambda

Are you confused? First of all, methods foo1 and foo2 can receive closures. What’s the difference between them? Then, what’s the difference between the three closures block, proc and lambda as parameters.

1. Difference between yield and block call

Both yield and block call can implement running closures. In terms of actual running effect, there is little difference. The main differences are as follows:

1.1 delivery and preservation of closures

Because the closure has been passed into the parameter, you can continue to pass or save it, for example:

Exampl 2:

Copy codeThe code is as follows:
 class A
      def foo1(&b)
         @proc = b
      end
      def foo2
          @proc.call if @proc
      end
   end

    a = A.new
    a.foo1 { puts “proc from foo1” }
    a.foo2

1.2 performance

Yield is not a method call, but a ruby keyword. Yield is about 1 times faster than block call.

2. The difference between block and proc, lambda

It’s very simple and direct. The purpose of introducing proc and lambda is to reuse and avoid duplicate definitions. For example, in example 1, use proc variables to store closures and avoid duplicate definitions of two blocks.

3. Difference between proc and lambda

This is probably the most puzzling part. From the behavior of example 1, their effect is the same. Why use two different expressions.

Copy codeThe code is as follows:
 proc = Proc.new { puts “foo in proc” }
   lambda_proc = lambda { puts “foo in lambda” }

It is true that for simple cases, such as example 1, their behavior is consistent, but there are two main differences:

1.1 parameter inspection

Example 3:

Copy codeThe code is as follows:
def foo
      x = 100
      yield x
   end

   proc = Proc.new { |a, b| puts “a is #{a.inspect} b is #{b.inspect}” }
   foo(&proc)

   lambda_proc1 = lambda { |a| puts “a is #{a.inspect}” }
   foo(&lambda_proc1)
   lambda_proc2 = lambda { |a, b| puts “a is #{a.inspect} b is #{b.inspect}” }
   foo(&lambda_proc2)

output

Copy codeThe code is as follows:
   》a is 100 b is nil
   》a is 100
   》ArgumentError: wrong number of arguments (1 for 2)
      …

It can be seen that proc does not check the number matching of parameters, but lambda will. If not, it will also report an exception. Therefore, for security reasons, lambda is preferred.

1.2 return to upper level

Example 4:

Copy codeThe code is as follows:
 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” }
     puts f.call # control does not leave bar here
     return “return from bar”
   end

   puts foo
   puts bar

output

Copy codeThe code is as follows:
   》return from foo from inside proc
   》return from lambda
   》return from bar

It can be seen that in proc, return is equivalent to the return in the closure environment, while lambda only returns the call closure environment.

Conclusion: closure is a powerful feature of ruby. There are subtle differences between block, proc and lambda in its several expressions. To use it well, we need to have a deep understanding of it.

Recommended Today

“Mybatis series” advanced application of mybatis

1. Associated query For example: since an order information can only be an order placed by one person, it is a one-to-one query starting from the order information query. If you start from the user information, it is a one to many query, because one user can place multiple orders. 1.1 one to one query […]