Understand ruby blocks, procs & lambdas

1396 words, 4 mins

什么是 blocks,procs 和 lambdas,如何使用?原理是什么?

什么是 blocks

Ruby的blocks类似于匿名函数,并且能作为参数传给其他函数.

Blocks有两种写法:

  • do end
  • {}

这两种都可以传递多个参数.举个例子

# {} 写法
[1, 2, 3].each { |i| p i }

# do end 写法
[1, 2, 3].each do |i|
  p i
end

什么是 yield

yield就是为了调用 blocks 的

举个例子

def show
  yield
end

show { p 'hello' }

yield 还可以多次执行

def show
  yield
  yield
  yield
end

show { p 'hello' }

yield 也可以传参,并且支持多个参数的传递,参数是传递到 blocks 中去的

def get_balance
  yield 1
  yield 2
  yield 3
end

get_balance { |i| p i * 10000 }

blocks 可以显式的传递

上面的 yield 方式算是隐式的传递

def explict_block(&block)
  block.call # same as yield
end

explict_block { p 'hello' }

如何判断是否传入了 block

如果不传block,执行 yield 会报错: no block given (yield)

可以通过block_given?方法判断

def implict_block
  return 'no block passed' unless block_given?
  yield
end

implict_block

什么是 lambda

lambda 可以定义好一个 block 以及其需要的参数.

语法是这样的:

say_something = -> { puts "This is a lambda" }

可以使用lambda替换->

调用可以用call

say_something = -> { puts "This is a lambda" }
say_something.call

也可以传递参数给 lambda

sum = ->(a, b) { a + b }
p sum.call(1, 2)

什么是 procs

lambda 和 procs 类似,只是 procs 的定义方式不同,lambda是一个特殊的 Proc对象, Proc传递参数的方式也不一样

my_proc = Proc.new { |x| p x }

Procreturn和 lambda 也不一样,lambda的return是退出自己,不影响调用方 而Procreturn是从当前方法中退出,跟直接在函数执行return一样

def call_proc
  puts "Before proc"
  my_proc = Proc.new { return 2 }
  my_proc.call
  puts "After proc"
end
p call_proc
# Prints "Before proc" but not "After proc"

Closures

Ruby 的Proc,lambda都有一个特殊的概念

这个概念叫闭包,可以访问到当前上下文的变量和方法,但是在闭包中即使修改了变量值,对上下文也不会影响

def call_proc(my_proc)
  count = 500
  my_proc.call
end
count   = 1
my_proc = Proc.new { puts count }
call_proc(my_proc)
p count  # What does this print?

这里虽然call_proc修改了 count,也并不会修改到外面的count