商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > 详解Ruby中的代码块及其参数传递

详解Ruby中的代码块及其参数传递

  发布于2026-06-29 阅读(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

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 的返回值。下面这个例子虽然不太实用,但能说明原理:

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 的经典实现,体会块如何让代码变得优雅:

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 代码块的核心要点。从基础声明到 yield 传参,再到高级的上下文探针,掌握这些之后,你写起 Ruby 来会感觉更加得心应手。

本文转载于:https://www.jb51.net/article/85253.htm 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

产品推荐

热门关注