What Is the Difference Between a Block, a Proc, and a Lambda in Ruby

原文链接:http://awaxman11.github.io/blog/2013/08/05/what-is-the-difference-between-a-block/

感觉写的很清晰易懂,虽然不一定全面,但是我感觉还是蛮不错的,转载了下。

What are blocks, procs, and lambdas?

Coder Talk: Examples of closures in ruby.

Plain old english: Ways of grouping code we want to run.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Block Examples  [1,2,3].each { |x| puts x*2 } # block is in between the curly braces  [1,2,3].each do |x|  puts x*2 # block is everything between the do and end end  # Proc Examples  p = Proc.new { |x| puts x*2 } [1,2,3].each(&p) # The '&' tells ruby to turn the proc into a block   proc = Proc.new { puts "Hello World" } proc.call # The body of the Proc object gets executed when called  # Lambda Examples  lam = lambda { |x| puts x*2 } [1,2,3].each(&lam)  lam = lambda { puts "Hello World" } lam.call 

While it looks like these are all very similar, there are subtle differences that I will cover below.

Differences between Blocks and Procs

  1. Procs are objects, blocks are not

    A proc (notice the lowercase p) is an instance of the Proc class.
    1
    
    p = Proc.new { puts "Hello World" } 
    This lets us call methods on it and assign it to variables. Procs can also return themselves.
    1
    2
    3
    4
    
    p.call # prints 'Hello World' p.class # returns 'Proc' a = p # a now equals p, a Proc instance p # returns a proc object '#<Proc:0x007f96b1a60eb0@(irb):46>' 

    In contrast, a block is just part of the *syntax* of a method call. It doesn’t mean anything on a standalone basis and can only appear in argument lists.
    1
    2
    3
    
    { puts "Hello World"} # syntax error  a = { puts "Hello World"} # syntax error [1,2,3].each {|x| puts x*2} # only works as part of the syntax of a method call 
  2. At most one block can appear in an argument list

    In contrast, you can pass multiple procs to methods.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    def multiple_procs(proc1, proc2)  proc1.call  proc2.call end  a = Proc.new { puts "First proc" } b = Proc.new { puts "Second proc" }  multiple_procs(a,b) 

Differences between Procs and Lambdas

Before I get into the differences between procs and lambdas, it is important to mention that they are both Proc objects.

1
2
3
4
5
proc = Proc.new { puts "Hello world" } lam = lambda { puts "Hello World" }  proc.class # returns 'Proc' lam.class # returns 'Proc' 

However, lambdas are a different ‘flavor’ of procs. This slight difference is shown when returning the objects.

1
2
proc # returns '#<Proc:0x007f96b1032d30@(irb):75>' lam # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>' 

The (lambda) notation is a reminder that while procs and lambdas are very similar, even both instances of the Proc class, they are also slightly different. Below are the key differences.

  1. Lambdas check the number of arguments, while procs do not

    1
    2
    3
    4
    
    lam = lambda { |x| puts x } # creates a lambda that takes 1 argument lam.call(2) # prints out 2 lam.call # ArgumentError: wrong number of arguments (0 for 1) lam.call(1,2,3) # ArgumentError: wrong number of arguments (3 for 1) 
    In contrast, procs don’t care if they are passed the wrong number of arguments.
    1
    2
    3
    4
    
    proc = Proc.new { |x| puts x } # creates a proc that takes 1 argument proc.call(2) # prints out 2 proc.call # returns nil proc.call(1,2,3) # prints out 1 and forgets about the extra arguments 
    As shown above, procs don’t freak out and raise errors if they are passed the wrong number of arguments. If the proc requires an argument but no argument is passed then the proc returns nil. If too many arguments are passed than it ignores the extra arguments.

  2. Lambdas and procs treat the ‘return’ keyword differently

    ‘return’ inside of a lambda triggers the code right outside of the lambda code
    1
    2
    3
    4
    5
    6
    7
    
    def lambda_test  lam = lambda { return }  lam.call  puts "Hello world" end  lambda_test # calling lambda_test prints 'Hello World' 
    ‘return’ inside of a proc triggers the code outside of the method where the proc is being executed
    1
    2
    3
    4
    5
    6
    7
    
    def proc_test  proc = Proc.new { return }  proc.call  puts "Hello world" end  proc_test # calling proc_test prints nothing 

So what is a closure?

Coder Talk: ‘A function or a reference to a function together with a referencing environment. Unlike a plain function, closures allow a function to access non-local variables even when invoked outside of its immediate lexical scope.’ – Wikipedia

Plain old english: Similar to a suitcase, it’s a group of code that when opened (i.e. called), contains whatever was in it when you packed it (i.e. created it).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Example of Proc objects preserving local context  def counter  n = 0  return Proc.new { n+= 1 } end  a = counter a.call # returns 1 a.call # returns 2  b = counter b.call # returns 1  a.call # returns 3 

Background Part 1: Lambda Calculus and Anonymous Functions

Lambda get its name from a type of calculus introduced in the 1930s to help investigate the foundations of mathematics. Lambda calculus helps make computable functions easier to study by simplifying its semantics. The most relevant of these simplifications is that is treats functions ‘anonymously’, meaning that no explicit names are given to functions.

1
2
sqsum(x,y) = x*x + y*y #<-- normal function (x,y) -> x*x + y*y #<-- anonymous function 

Generally speaking, in programming the term lambda refers to anonymous functions. These anonymous functions are very common and explicit in some languages (i.e. Javascript) and implicit in others (i.e. Ruby).

Background Part 2: Where Does the Name Proc Come From

Proc is short for procedure, which is a set of instructions packaged as a unit to perform a specific task. In different languages these may be called functions, routines, methods, or the generic term callable units. They are usually made to be called multiple times and from multiple times in a program.

Summary Differences

  1. Procs are objects, blocks are not
  2. At most one block can appear in an argument list
  3. Lambdas check the number of arguments, while procs do not
  4. Lambdas and procs treat the ‘return’ keyword differently

你可能感兴趣的:(Ruby,lambda,methods)