位置:首页 > Ruby > Ruby代码块与参数传递机制详解

Ruby代码块与参数传递机制详解

时间:2026-08-18  |  作者:骑光打字机  |  阅读:0

Ruby 的代码块(Block) 是这门语言最优雅的特性之一。很多初学者刚接触时,容易被它的语法绕晕。

别担心,下面我们一步步拆解块的定义、参数传递、yield 机制,以及几个进阶用法。

一、块的声明

块可以跟在函数调用后面,用 {} 或者 do..end 包裹起来。

一个简单原则:单行语句用 {},多行语句用 do..end

比如:

(1..4).each{|v| print "#{v} "} #输出1 2 3 4 

块参数的写法

块还可以带参数,参数用 || 包裹。

参数可以声明多个。它们的具体含义,是在函数内部通过 yield 传递时定义的,后面会详细讲。

二、块内变量的访问

块内部默认可以访问块外部的变量。

但块内定义的变量,如果没有同名外部变量,则只在块内可见。

看下面这个例子:

sum = 0 
(1..5).each do |v| 
  name = 'smile' #name属于块内变量
  sum += v 
end 
p sum   #输出15,sum已改变
p name  #Error! name不可访问

块内变量的隔离

这种“全局可见”特性,有时会导致意外修改外部变量。

好在 Ruby 1.9 之后提供了一种安全机制:在块参数后面用分号声明块内变量,这些变量会被隔离。

name = 'outside' 
sum = 0 
(1..5).each do |v;name| #分号之后的name是块内变量
  name = 'inside' 
  sum += v 
end 
p sum   #输出15
p name  #输出outside,外部没被改变

三、yield 语句

块到底是怎么被调用的?答案就是 yield

在函数体内部使用 yield,就会执行该函数后面跟着的块。

先看一个最简单的例子:

def threeTime 
  yield 
  yield 
  yield 
end 
threeTime{p 'Hello world!'}  # 输出三行 Hello world!

yield 传递参数

块参数的来源,其实就是 yield 的参数。

比如:

def takeBlock(p1) 
 if block_given   # 判断是否有块
  yield(p1) 
 else 
  p1 
 end 
end

takeBlock("no block")                    # 输出"no block"
takeBlock("no block") { |s| s.sub(/no /, '') } # 输出"block"

yield 的返回值

反过来,块也能返回值给 yield。

块中最后一句表达式的值,会自动成为 yield 的返回值。

下面这个例子虽然不太实用,但能说明原理:

def nTime 
 i = yield   # 第一次调用获取块返回值
 (0..i).each {|v| yield(v)} 
end 
nTime do |v| 
 print "#{v} " if v 
 9   # 这个9就是yield的返回值
end 
# 输出 1 2 3 4 5 6 7 8 9

更常见的写法

更常见的写法,是把参数直接传给方法:

def nTime(n) 
 (0..n).each {|v| yield(v)} 
end 
nTime(9) do |v| 
 print "#{v} " 
end

Array#find 的经典实现

来看一下 Array#find 的经典实现,体会块如何让代码变得优雅:

class Array 
 def find 
  for i in 0...size 
   value = self[i] 
   return value if yield(value) 
  end 
  return nil 
 end 
end 
[1, 3, 5, 7, 9].find {|v| v > 5 } # 返回第一个大于5的数,即7

正是因为块的存在,Ruby 中传统的 for 循环大幅减少。

代码更接近自然语言,写起来也更有乐趣。

四、传递块的另一种方式

除了直接跟在方法后面,还可以把 Proc 对象通过 & 转换成块传进去:

def fun   # 不带参数
 yield 
end 
proc = ->{p 'haha'} 
fun &proc

def fun2(x) # 带参数
 yield x 
end 
proc2 = ->(x){p x} 
fun2 1, &proc2

五、instance_eval() 和 instance_exec()

Ruby 还提供了一个非常酷的特性。

通过 Object#instance_eval()Object#instance_exec() 插入代码块,可以深入到对象的内部上下文进行操作。

这就像给对象做了一次“上下文探针”。调试或者测试行为时尤其好用:

class MyClass 
 def initialize 
  @v = 1; 
 end 
end 
obj = MyClass.new 
obj.instance_eval do 
 puts self       # => # 
 puts @v        # => 1 
end 
obj.instance_exec(5) { |x| puts x * @v } # => 5

总结

以上就是 Ruby 代码块的核心要点。

  • 块可以用 {}do..end 声明
  • 块可以接收参数,也会受到变量作用域影响
  • yield 负责调用块,并可完成参数传递与返回值获取
  • Proc 可以通过 & 转换后传递
  • instance_eval()instance_exec() 可以操作对象内部上下文

掌握这些之后,你写起 Ruby 来会感觉更加得心应手。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多