Ruby 的 yield 浅析

今天在书上看到这么一个例子,虽然我多多少少对 Ruby 的 block 有一些了解,但是第一次见到下面这种时,还是有点懵的:

# Ruby

def block_args_test
  yield()
  yield(1)
  yield(1, 2, 3)
end

puts "通过 |a| 接收块变量"
block_args_test do |a|
  p [a]
end
puts

puts "通过 |a, b, c| 接收块变量"
block_args_test do |a, b, c|
  p [a, b, c]
end
puts

puts "通过 |*a| 接收块变量"
block_args_test do |*a|
  p [a]
end
puts

------------------------------------------------------------
通过 |a| 接收块变量
[nil]
[1]
[1]

通过 |a, b, c| 接收块变量
[nil, nil, nil]
[1, nil, nil]
[1, 2, 3]

通过 |*a| 接收块变量
[[]]
[[1]]
[[1, 2, 3]]

所以,为了啃下这块硬骨头,先从一个简单的例子讲起吧!

不带参的块

# Ruby
# 定义块
def myloop
  while true
    yield     # 执行块
  end
end

num = 1
myloop do
  puts "num is #{num}"
  break  if num > 10
  num *= 2
end

------------------------------------------------------------
num is 1
num is 2
num is 4
num is 8
num is 16

首先是定义块,在定义好块以后,使用块的 do ~ end 语句时,其实质是把块里边的代码:

  puts "num is #{num}"
  break  if num > 10
  num *= 2

放到了 yield 的地方去执行,所以这儿是比较好理解的。

携带参的块

我们把上面的简单例子稍微改造一下,改成可以传参给 block,下面还是可以得到同上面一样的输出。

# Ruby
# 定义块
def myloop
  while true
    yield(2)     # 执行块
  end
end

num = 1
myloop do |i|
  puts "num is #{num}"
  break  if num > 10
  num *= i
end

------------------------------------------------------------
num is 1
num is 2
num is 4
num is 8
num is 16

虽然比不传参的稍微复杂一点,但是我相信大家还是能看懂的。

传参数目不一致

现在,我们再复杂化一点,块定义的时候设定两个参数,但是 block  执行的时候只给块传一个参数。

# Ruby
# 定义块
def myloop
  while true
    yield(2, 3)     # 执行块
  end
end

num = 1
myloop do |i|
  puts "num is #{num}"
  break  if num > 10
  num *= i
end

------------------------------------------------------------
num is 1
num is 2
num is 4
num is 8
num is 16

还是一样的结果,似乎多的参数对执行结果并没有什么影响。到现在,我们基本上能把最开始的例子解释一下了。

结果解释

# Ruby

def block_args_test
  yield()
  yield(1)
  yield(1, 2, 3)
end

puts "通过 |a| 接收块变量"
block_args_test do |a|
  p [a]
end
puts

puts "通过 |a, b, c| 接收块变量"
block_args_test do |a, b, c|
  p [a, b, c]
end
puts

puts "通过 |*a| 接收块变量"
block_args_test do |*a|
  p [a]
end
puts

------------------------------------------------------------
通过 |a| 接收块变量
[nil]  # yield() 没传递参数,所以 a 没接收到参数,a 为 nil
[1]    # yield(1) 传递一个参数,所以 a 接收到传递的参数,a 为 1
[1]    # yield(1, 2, 3) 传递三个参数,但是只有一个块变量接收,所以接收到第一个参数,a 为 1

通过 |a, b, c| 接收块变量
[nil, nil, nil]   # yield() 没传递参数,所以三个块变量 a, b, c 均为 nil 
[1, nil, nil]     # yield() 传递一个参数,所以 a 接收到传递的参数,a 为 1,剩下 2 个块变量 b, c 均为 nil
[1, 2, 3]         # yield() 传递三个个参数,a, b, c 三个块变量分别接收一个

通过 |*a| 接收块变量
# *a 是匹配任意个的意思,所以无论 yield 传递过来多少参数,块变量都会将多个参数变成一个数组
# 我们可以来个下面的简单例子印证一下
# [20] pry(main)> def test(*args)
# [20] pry(main)*   p [args]  
# [20] pry(main)* end  
# => :test
# [21] pry(main)> test(1, 2, 3)
# [[1, 2, 3]]
# => [[1, 2, 3]]

[[]]              # yield() 没传递参数,所以 a 为 []
[[1]]             # yield() 传递一个参数,所以 a 为 [1]
[[1, 2, 3]]       # yield() 传递三个个参数,匹配三个参数以后,所以 a 为 [1, 2, 3]

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值