1. 什么是块?
块是一段匿名的代码,通常与方法调用一起使用,类似其他语言中的匿名函数或闭包。块有两种形式:
- 用
do ... end
包裹(多行)。 - 用
{ ... }
包裹(单行)。
块常用于:
- 遍历集合(如
each
)。 - 执行回调。
- 自定义方法行为。
2. 块的基本语法
用 do ... end
3.times do
puts "你好"
end
输出:
你好
你好
你好
用 { ... }
3.times { puts "你好" }
输出同上。
带参数
块可以接受参数,用 |
包裹:
[1, 2, 3].each do |number|
puts "数字:#{number}"
end
输出:
数字:1
数字:2
数字:3
3. 块与方法的关系
方法可以通过 yield
调用传入的块,或者显式接收块参数(用 &
)。
使用 yield
def say_hello
puts "开始"
yield
puts "结束"
end
say_hello do
puts "你好,世界!"
end
输出:
开始
你好,世界!
结束
带参数的 yield
def greet(name)
yield(name)
end
greet("张三") do |n|
puts "你好,#{n}!"
end
输出:
你好,张三!
检查块是否存在
用 block_given?
判断是否传入了块:
def try_block
if block_given?
yield
else
puts "没有传入块"
end
end
try_block { puts "这是一个块" } # 这是一个块
try_block # 没有传入块
4. 显式块参数
方法可以用 &block
显式接收块,并将其作为对象处理。
示例
def run_block(&block)
puts "准备执行块"
block.call
puts "块执行完毕"
end
run_block { puts "我在块中" }
输出:
准备执行块
我在块中
块执行完毕
带参数
def pass_value(&block)
block.call("李四")
end
pass_value { |name| puts "你好,#{name}!" }
输出:
你好,李四!
5. 常见块用法
遍历集合
- 数组:
水果 = ["苹果", "香蕉", "橙子"]
水果.each_with_index do |fruit, index|
puts "#{index + 1}. #{fruit}"
end
输出:
1. 苹果
2. 香蕉
3. 橙子
- 哈希:
学生 = { 姓名: "王五", 年龄: 20 }
学生.each do |键, 值|
puts "#{键}:#{值}"
end
输出:
姓名:王五
年龄:20
转换数据
用 map
:
numbers = [1, 2, 3]
doubled = numbers.map do |n|
n * 2
end
puts doubled # [2, 4, 6]
过滤数据
用 select
:
numbers = [1, 2, 3, 4, 5]
even = numbers.select { |n| n % 2 == 0 }
puts even # [2, 4]
6. 块的返回值
块的最后一行是返回值,常用于方法处理:
def calculate
result = yield(5, 3)
puts "结果:#{result}"
end
calculate { |a, b| a + b } # 结果:8
calculate { |a, b| a * b } # 结果:15
7. 中文支持示例
块支持中文变量和输出:
任务 = ["写代码", "开会", "休息"]
任务.each do |task|
puts "今天要 #{task}"
end
输出:
今天要 写代码
今天要 开会
今天要 休息
8. 实践案例
案例 1:计时器
def timer
start = Time.now
yield
end_time = Time.now
puts "耗时:#{end_time - start} 秒"
end
timer do
5.times { puts "执行中..." }
end
输出(示例):
执行中...
执行中...
执行中...
执行中...
执行中...
耗时:0.00234 秒
案例 2:自定义迭代
def repeat(n)
n.times { |i| yield(i + 1) }
end
repeat(3) do |count|
puts "第 #{count} 次问候:你好!"
end
输出:
第 1 次问候:你好!
第 2 次问候:你好!
第 3 次问候:你好!
案例 3:任务处理器
class TaskProcessor
def process(tasks)
tasks.each do |task|
yield(task) if block_given?
end
end
end
processor = TaskProcessor.new
任务 = ["买菜", "做饭", "吃饭"]
processor.process(任务) do |task|
puts "正在处理:#{task}"
end
输出:
正在处理:买菜
正在处理:做饭
正在处理:吃饭
9. 块 vs Proc vs Lambda
简单对比
- 块:匿名代码,直接传递给方法。
- Proc:块的对象化形式,可存储和传递。
- Lambda:类似 Proc,但更严格(参数检查,返回行为)。
示例
# Proc
my_proc = Proc.new { |x| puts "Proc: #{x}" }
my_proc.call(5) # Proc: 5
# Lambda
my_lambda = lambda { |x| puts "Lambda: #{x}" }
my_lambda.call(10) # Lambda: 10
方法接收 Proc
def run_proc(&block)
block.call
end
proc = Proc.new { puts "你好" }
run_proc(&proc) # 你好
10. 注意事项
- 性能:块是轻量级的,但大量嵌套可能影响可读性。
- 作用域:块可以访问外部变量(闭包特性):
x = 0
3.times { x += 1 }
puts x # 3
- 参数匹配:块参数数量应与
yield
匹配,否则可能报错。
下一步
- 练习:告诉我你想用块做什么(遍历、自定义方法等),我可以设计一个例子。
- 问题解答:对块用法有疑问吗?直接问我!
- 深入学习:想了解 Proc、Lambda 或块在框架中的应用吗?我可以继续讲解。
你现在想做什么?写代码、问问题,还是其他?
发表回复